menu.c 159 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2 3 4 5
 * Menu functions
 *
 * Copyright 1993 Martin Ayotte
 * Copyright 1994 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
6
 * Copyright 1997 Morten Welinder
7
 * Copyright 2005 Maxime Bellengé
8
 * Copyright 2006 Phil Krylov
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
23
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
24 25 26 27 28

/*
 * Note: the style MF_MOUSESELECT is used to mark popup items that
 * have been selected, i.e. their popup menu is currently displayed.
 * This is probably not the meaning this style has in MS-Windows.
29
 *
30 31 32 33 34
 * Note 2: where there is a difference, these menu API's are according
 * the behavior of Windows 2k and Windows XP. Known differences with
 * Windows 9x/ME are documented in the comments, in case an application
 * is found to depend on the old behavior.
 * 
35 36 37 38 39
 * TODO:
 *    implements styles :
 *        - MNS_AUTODISMISS
 *        - MNS_DRAGDROP
 *        - MNS_MODELESS
Alexandre Julliard's avatar
Alexandre Julliard committed
40
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
41

42
#include "config.h"
43 44
#include "wine/port.h"

45
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
46
#include <string.h>
47

48 49
#define OEMRESOURCE

50
#include "windef.h"
51
#include "winbase.h"
52
#include "wingdi.h"
53
#include "winnls.h"
54
#include "wine/server.h"
55
#include "wine/unicode.h"
56
#include "wine/exception.h"
57
#include "win.h"
58
#include "controls.h"
59
#include "user_private.h"
60
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
61

62 63
WINE_DEFAULT_DEBUG_CHANNEL(menu);
WINE_DECLARE_DEBUG_CHANNEL(accel);
Alexandre Julliard's avatar
Alexandre Julliard committed
64

Alexandre Julliard's avatar
Alexandre Julliard committed
65 66 67 68 69
/* internal popup menu window messages */

#define MM_SETMENUHANDLE	(WM_USER + 0)
#define MM_GETMENUHANDLE	(WM_USER + 1)

Alexandre Julliard's avatar
Alexandre Julliard committed
70
/* Menu item structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
71 72
typedef struct {
    /* ----------- MENUITEMINFO Stuff ----------- */
73
    UINT fType;			/* Item type. */
74
    UINT fState;		/* Item state.  */
75
    UINT_PTR wID;		/* Item id.  */
76
    HMENU hSubMenu;		/* Pop-up menu.  */
77
    HBITMAP hCheckBit;		/* Bitmap when checked.  */
78
    HBITMAP hUnCheckBit;	/* Bitmap when unchecked.  */
79
    LPWSTR text;		/* Item text. */
80 81
    ULONG_PTR dwItemData;	/* Application defined.  */
    LPWSTR dwTypeData;		/* depends on fMask */
82
    HBITMAP hbmpItem;		/* bitmap */
Alexandre Julliard's avatar
Alexandre Julliard committed
83
    /* ----------- Wine stuff ----------- */
84 85
    RECT      rect;		/* Item area (relative to menu window) */
    UINT      xTab;		/* X position of text after Tab */
Rein Klazes's avatar
Rein Klazes committed
86 87
    SIZE   bmpsize;             /* size needed for the HBMMENU_CALLBACK
                                 * bitmap */ 
Alexandre Julliard's avatar
Alexandre Julliard committed
88 89 90
} MENUITEM;

/* Popup menu structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
91
typedef struct {
92
    struct user_object obj;
Alexandre Julliard's avatar
Alexandre Julliard committed
93 94 95
    WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
    WORD	Width;        /* Width of the whole menu */
    WORD	Height;       /* Height of the whole menu */
96
    UINT        nItems;       /* Number of items in the menu */
97 98 99
    HWND        hWnd;         /* Window containing the menu */
    MENUITEM    *items;       /* Array of menu items */
    UINT        FocusedItem;  /* Currently focused item */
100
    HWND	hwndOwner;    /* window receiving the messages for ownerdraw */
101
    BOOL        bTimeToHide;  /* Request hiding when receiving a second click in the top-level menu item */
102 103 104
    BOOL        bScrolling;   /* Scroll arrows are active */
    UINT        nScrollPos;   /* Current scroll position */
    UINT        nTotalHeight; /* Total height of menu items inside menu */
105
    /* ------------ MENUINFO members ------ */
106 107
    DWORD	dwStyle;	/* Extended menu style */
    UINT	cyMax;		/* max height of the whole menu, 0 is screen height */
108 109 110
    HBRUSH	hbrBack;	/* brush for menu background */
    DWORD	dwContextHelpID;
    DWORD	dwMenuData;	/* application defined value */
111
    HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
112
    WORD        textOffset;     /* Offset of text when items have both bitmaps and text */
Alexandre Julliard's avatar
Alexandre Julliard committed
113 114
} POPUPMENU, *LPPOPUPMENU;

Alexandre Julliard's avatar
Alexandre Julliard committed
115 116
/* internal flags for menu tracking */

117 118 119
#define TF_ENDMENU              0x10000
#define TF_SUSPENDPOPUP         0x20000
#define TF_SKIPREMOVE           0x40000
Alexandre Julliard's avatar
Alexandre Julliard committed
120 121 122

typedef struct
{
123 124 125 126 127
    UINT	trackFlags;
    HMENU	hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
    HMENU	hTopMenu;     /* initial menu */
    HWND	hOwnerWnd;    /* where notifications are sent */
    POINT	pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
128 129
} MTRACKER;

Alexandre Julliard's avatar
Alexandre Julliard committed
130 131
#define MENU_MAGIC   0x554d  /* 'MU' */

Alexandre Julliard's avatar
Alexandre Julliard committed
132 133 134
#define ITEM_PREV		-1
#define ITEM_NEXT		 1

Alexandre Julliard's avatar
Alexandre Julliard committed
135 136 137
  /* Internal MENU_TrackMenu() flags */
#define TPM_INTERNAL		0xF0000000
#define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
138
#define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
139

140 141 142
  /* Space between 2 columns */
#define MENU_COL_SPACE 4

143 144 145 146
  /*  top and bottom margins for popup menus */
#define MENU_TOP_MARGIN 3
#define MENU_BOTTOM_MARGIN 2

147 148 149 150 151
/* maximum allowed depth of any branch in the menu tree.
 * This value is slightly larger than in windows (25) to
 * stay on the safe side. */
#define MAXMENUDEPTH 30

Alexandre Julliard's avatar
Alexandre Julliard committed
152
  /* (other menu->FocusedItem values give the position of the focused item) */
Alexandre Julliard's avatar
Alexandre Julliard committed
153 154
#define NO_SELECTED_ITEM  0xffff

Alexandre Julliard's avatar
Alexandre Julliard committed
155 156 157
#define MENU_ITEM_TYPE(flags) \
  ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))

158
/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
Alexandre Julliard's avatar
Alexandre Julliard committed
159
#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160
#define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
Alexandre Julliard's avatar
Alexandre Julliard committed
161

Alexandre Julliard's avatar
Alexandre Julliard committed
162
#define IS_SYSTEM_MENU(menu)  \
163
	(!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
Alexandre Julliard's avatar
Alexandre Julliard committed
164

165 166 167 168 169
#define MENUITEMINFO_TYPE_MASK \
		(MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
		MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
		MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
#define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
Alexandre Julliard's avatar
Alexandre Julliard committed
170
#define STATE_MASK (~TYPE_MASK)
171
#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
Alexandre Julliard's avatar
Alexandre Julliard committed
172

173 174
#define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)

175 176
static SIZE     menucharsize;
static UINT     ODitemheight; /* default owner drawn item height */      
177

Alexandre Julliard's avatar
Alexandre Julliard committed
178
/* Use global popup window because there's no way 2 menus can
179
 * be tracked at the same time.  */
180
static HWND top_popup;
181
static HMENU top_popup_hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
182

Alexandre Julliard's avatar
Alexandre Julliard committed
183
  /* Flag set by EndMenu() to force an exit from menu tracking */
184
static BOOL fEndMenu = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
185

186
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187

188 189
static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);

190 191 192 193 194
/*********************************************************************
 * menu class descriptor
 */
const struct builtin_class_descr MENU_builtin_class =
{
195
    (LPCWSTR)POPUPMENU_CLASS_ATOM,  /* name */
196
    CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
197
    WINPROC_MENU,                  /* proc */
198
    sizeof(HMENU),                 /* extra */
199
    IDC_ARROW,                     /* cursor */
200
    (HBRUSH)(COLOR_MENU+1)         /* brush */
201 202
};

Alexandre Julliard's avatar
Alexandre Julliard committed
203

Alexandre Julliard's avatar
Alexandre Julliard committed
204 205 206 207 208 209
/***********************************************************************
 *           debug_print_menuitem
 *
 * Print a menuitem in readable form.
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
210
#define debug_print_menuitem(pre, mp, post) \
211
    do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
Alexandre Julliard's avatar
Alexandre Julliard committed
212

Alexandre Julliard's avatar
Alexandre Julliard committed
213
#define MENUOUT(text) \
214
  TRACE("%s%s", (count++ ? "," : ""), (text))
Alexandre Julliard's avatar
Alexandre Julliard committed
215 216 217 218 219 220

#define MENUFLAG(bit,text) \
  do { \
    if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
  } while (0)

221
static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
Alexandre Julliard's avatar
Alexandre Julliard committed
222
				    const char *postfix)
Alexandre Julliard's avatar
Alexandre Julliard committed
223
{
224
    static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225
    "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 227
    "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
    "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228
    TRACE("%s ", prefix);
Alexandre Julliard's avatar
Alexandre Julliard committed
229
    if (mp) {
230
        UINT flags = mp->fType;
231
        TRACE( "{ ID=0x%lx", mp->wID);
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
        if ( mp->hSubMenu)
            TRACE( ", Sub=%p", mp->hSubMenu);
        if (flags) {
            int count = 0;
            TRACE( ", fType=");
            MENUFLAG( MFT_SEPARATOR, "sep");
            MENUFLAG( MFT_OWNERDRAW, "own");
            MENUFLAG( MFT_BITMAP, "bit");
            MENUFLAG(MF_POPUP, "pop");
            MENUFLAG(MFT_MENUBARBREAK, "barbrk");
            MENUFLAG(MFT_MENUBREAK, "brk");
            MENUFLAG(MFT_RADIOCHECK, "radio");
            MENUFLAG(MFT_RIGHTORDER, "rorder");
            MENUFLAG(MF_SYSMENU, "sys");
            MENUFLAG(MFT_RIGHTJUSTIFY, "right");  /* same as MF_HELP */
            if (flags)
                TRACE( "+0x%x", flags);
        }
        flags = mp->fState;
        if (flags) {
            int count = 0;
            TRACE( ", State=");
            MENUFLAG(MFS_GRAYED, "grey");
            MENUFLAG(MFS_DEFAULT, "default");
            MENUFLAG(MFS_DISABLED, "dis");
            MENUFLAG(MFS_CHECKED, "check");
            MENUFLAG(MFS_HILITE, "hi");
            MENUFLAG(MF_USECHECKBITMAPS, "usebit");
            MENUFLAG(MF_MOUSESELECT, "mouse");
            if (flags)
                TRACE( "+0x%x", flags);
        }
        if (mp->hCheckBit)
            TRACE( ", Chk=%p", mp->hCheckBit);
        if (mp->hUnCheckBit)
            TRACE( ", Unc=%p", mp->hUnCheckBit);
        if (mp->text)
            TRACE( ", Text=%s", debugstr_w(mp->text));
        if (mp->dwItemData)
            TRACE( ", ItemData=0x%08lx", mp->dwItemData);
        if (mp->hbmpItem)
        {
            if( IS_MAGIC_BITMAP(mp->hbmpItem))
                TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
            else
                TRACE( ", hbitmap=%p", mp->hbmpItem);
        }
        TRACE( " }");
    } else
        TRACE( "NULL");
282
    TRACE(" %s\n", postfix);
Alexandre Julliard's avatar
Alexandre Julliard committed
283 284 285 286 287
}

#undef MENUOUT
#undef MENUFLAG

288 289 290 291 292 293

/***********************************************************************
 *           MENU_GetMenu
 *
 * Validate the given menu handle and returns the menu structure pointer.
 */
294
static POPUPMENU *MENU_GetMenu(HMENU hMenu)
295
{
296 297 298
    POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );

    if (menu == OBJ_OTHER_PROCESS)
299
    {
300 301
        WARN( "other process menu %p?\n", hMenu);
        return NULL;
302
    }
303 304
    if (menu) release_user_handle_ptr( menu );  /* FIXME! */
    else WARN("invalid menu handle=%p\n", hMenu);
305 306 307
    return menu;
}

308 309 310 311 312 313 314 315
/***********************************************************************
 *           get_win_sys_menu
 *
 * Get the system menu of a window
 */
static HMENU get_win_sys_menu( HWND hwnd )
{
    HMENU ret = 0;
316
    WND *win = WIN_GetPtr( hwnd );
317
    if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
318 319
    {
        ret = win->hSysMenu;
320
        WIN_ReleasePtr( win );
321 322 323 324
    }
    return ret;
}

325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
/***********************************************************************
 *           get_menu_font
 */
static HFONT get_menu_font( BOOL bold )
{
    static HFONT hMenuFont, hMenuFontBold;

    HFONT ret = bold ? hMenuFontBold : hMenuFont;

    if (!ret)
    {
        NONCLIENTMETRICSW ncm;
        HFONT prev;

        ncm.cbSize = sizeof(NONCLIENTMETRICSW);
        SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);

        if (bold)
        {
            ncm.lfMenuFont.lfWeight += 300;
            if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
        }
        if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
        prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
                                                  ret, NULL );
        if (prev)
        {
            /* another thread beat us to it */
            DeleteObject( ret );
            ret = prev;
        }
    }
    return ret;
}

/***********************************************************************
 *           get_arrow_bitmap
 */
static HBITMAP get_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
    return arrow_bitmap;
}

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
/***********************************************************************
 *           get_down_arrow_bitmap
 */
static HBITMAP get_down_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_down_arrow_inactive_bitmap
 */
static HBITMAP get_down_arrow_inactive_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_up_arrow_bitmap
 */
static HBITMAP get_up_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
    return arrow_bitmap;
}

/***********************************************************************
 *           get_up_arrow_inactive_bitmap
 */
static HBITMAP get_up_arrow_inactive_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
    return arrow_bitmap;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
415 416
/***********************************************************************
 *           MENU_CopySysPopup
Alexandre Julliard's avatar
Alexandre Julliard committed
417
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
418
 * Return the default system menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
419
 */
420
static HMENU MENU_CopySysPopup(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
421
{
422
    static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423
    HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
Alexandre Julliard's avatar
Alexandre Julliard committed
424

Alexandre Julliard's avatar
Alexandre Julliard committed
425
    if( hMenu ) {
426 427
        MENUINFO minfo;
        MENUITEMINFOW miteminfo;
428
        POPUPMENU* menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
429
        menu->wFlags |= MF_SYSMENU | MF_POPUP;
430
        /* decorate the menu with bitmaps */
431 432 433 434 435 436 437 438 439 440 441 442 443 444
        minfo.cbSize = sizeof( MENUINFO);
        minfo.dwStyle = MNS_CHECKORBMP;
        minfo.fMask = MIM_STYLE;
        SetMenuInfo( hMenu, &minfo);
        miteminfo.cbSize = sizeof( MENUITEMINFOW);
        miteminfo.fMask = MIIM_BITMAP;
        miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
        SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
        SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
        SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
        SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
445
	SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
446
    }
447
    else
448
	ERR("Unable to load default system menu\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
449

450
    TRACE("returning %p.\n", hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
451 452

    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
453 454 455
}


Alexandre Julliard's avatar
Alexandre Julliard committed
456 457
/**********************************************************************
 *           MENU_GetSysMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
458
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
459
 * Create a copy of the system menu. System menu in Windows is
460
 * a special menu bar with the single entry - system menu popup.
461 462
 * This popup is presented to the outside world as a "system menu".
 * However, the real system menu handle is sometimes seen in the
463
 * WM_MENUSELECT parameters (and Word 6 likes it this way).
Alexandre Julliard's avatar
Alexandre Julliard committed
464
 */
465
static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
466
{
467
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
468

469
    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
470
    if ((hMenu = CreateMenu()))
Alexandre Julliard's avatar
Alexandre Julliard committed
471
    {
472
	POPUPMENU *menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
473
	menu->wFlags = MF_SYSMENU;
474
	menu->hWnd = WIN_GetFullHandle( hWnd );
475
	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
476

477
	if (!hPopupMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
478
	    hPopupMenu = MENU_CopySysPopup();
Alexandre Julliard's avatar
Alexandre Julliard committed
479

Alexandre Julliard's avatar
Alexandre Julliard committed
480 481
	if (hPopupMenu)
	{
482 483 484
            if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
                DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);

485
	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
486
                         (UINT_PTR)hPopupMenu, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
487

Alexandre Julliard's avatar
Alexandre Julliard committed
488 489
            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
            menu->items[0].fState = 0;
490
            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
Alexandre Julliard's avatar
Alexandre Julliard committed
491

492
	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
493 494
	    return hMenu;
	}
495
	DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
496
    }
497
    ERR("failed to load system menu!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
498
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
499 500 501
}


Alexandre Julliard's avatar
Alexandre Julliard committed
502 503 504 505 506
/***********************************************************************
 *           MENU_InitSysMenuPopup
 *
 * Grey the appropriate items in System menu.
 */
507
static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
Alexandre Julliard's avatar
Alexandre Julliard committed
508
{
509
    BOOL gray;
Alexandre Julliard's avatar
Alexandre Julliard committed
510 511

    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
512
    EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
513
    gray = ((style & WS_MAXIMIZE) != 0);
514
    EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
515
    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
516
    EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
517
    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
518
    EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
519
    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
520
    EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
521
    gray = (clsStyle & CS_NOCLOSE) != 0;
522 523 524 525

    /* The menu item must keep its state if it's disabled */
    if(gray)
	EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
Alexandre Julliard's avatar
Alexandre Julliard committed
526 527 528
}


Alexandre Julliard's avatar
Alexandre Julliard committed
529 530
/******************************************************************************
 *
531 532
 *   UINT  MENU_GetStartOfNextColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
533 534 535
 *
 *****************************************************************************/

536 537
static UINT  MENU_GetStartOfNextColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
538
{
539 540
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
541 542 543 544

    if(!menu)
	return NO_SELECTED_ITEM;

545
    i = menu->FocusedItem + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
546 547 548 549
    if( i == NO_SELECTED_ITEM )
	return i;

    for( ; i < menu->nItems; ++i ) {
550
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
551 552 553 554 555 556 557 558 559
	    return i;
    }

    return NO_SELECTED_ITEM;
}


/******************************************************************************
 *
560 561
 *   UINT  MENU_GetStartOfPrevColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
562 563 564
 *
 *****************************************************************************/

565 566
static UINT  MENU_GetStartOfPrevColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
567
{
568
    POPUPMENU *menu = MENU_GetMenu(hMenu);
569
    UINT  i;
Alexandre Julliard's avatar
Alexandre Julliard committed
570 571 572 573 574 575 576 577 578 579

    if( !menu )
	return NO_SELECTED_ITEM;

    if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
	return NO_SELECTED_ITEM;

    /* Find the start of the column */

    for(i = menu->FocusedItem; i != 0 &&
580
	 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582 583 584 585 586
	--i); /* empty */

    if(i == 0)
	return NO_SELECTED_ITEM;

    for(--i; i != 0; --i) {
587
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
588 589 590
	    break;
    }

591
    TRACE("ret %d.\n", i );
Alexandre Julliard's avatar
Alexandre Julliard committed
592 593 594 595 596 597

    return i;
}



Alexandre Julliard's avatar
Alexandre Julliard committed
598 599 600 601 602 603
/***********************************************************************
 *           MENU_FindItem
 *
 * Find a menu item. Return a pointer on the item, and modifies *hmenu
 * in case the item was in a sub-menu.
 */
604
static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
605 606
{
    POPUPMENU *menu;
607
    MENUITEM *fallback = NULL;
608
    UINT fallback_pos = 0;
609
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
610

611
    if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
612 613 614
    if (wFlags & MF_BYPOSITION)
    {
	if (*nPos >= menu->nItems) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
615
	return &menu->items[*nPos];
Alexandre Julliard's avatar
Alexandre Julliard committed
616 617 618
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
619
        MENUITEM *item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
620 621
	for (i = 0; i < menu->nItems; i++, item++)
	{
622
	    if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
623
	    {
624
		HMENU hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
625 626 627 628 629 630
		MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
		if (subitem)
		{
		    *hmenu = hsubmenu;
		    return subitem;
		}
631 632 633 634 635 636
		else if (item->wID == *nPos)
		{
		    /* fallback to this item if nothing else found */
		    fallback_pos = i;
		    fallback = item;
		}
637 638 639 640 641
	    }
	    else if (item->wID == *nPos)
	    {
		*nPos = i;
		return item;
Alexandre Julliard's avatar
Alexandre Julliard committed
642 643 644
	    }
	}
    }
645 646 647 648

    if (fallback)
        *nPos = fallback_pos;

649
    return fallback;
Alexandre Julliard's avatar
Alexandre Julliard committed
650 651
}

652 653 654
/***********************************************************************
 *           MENU_FindSubMenu
 *
655
 * Find a Sub menu. Return the position of the submenu, and modifies
656 657 658
 * *hmenu in case it is found in another sub-menu.
 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
 */
659
static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
660 661 662 663
{
    POPUPMENU *menu;
    UINT i;
    MENUITEM *item;
664
    if (((*hmenu)==(HMENU)0xffff) ||
665
            (!(menu = MENU_GetMenu(*hmenu))))
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
        return NO_SELECTED_ITEM;
    item = menu->items;
    for (i = 0; i < menu->nItems; i++, item++) {
        if(!(item->fType & MF_POPUP)) continue;
        if (item->hSubMenu == hSubTarget) {
            return i;
        }
        else  {
            HMENU hsubmenu = item->hSubMenu;
            UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
            if (pos != NO_SELECTED_ITEM) {
                *hmenu = hsubmenu;
                return pos;
            }
        }
    }
    return NO_SELECTED_ITEM;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
685 686 687 688 689 690
/***********************************************************************
 *           MENU_FreeItemData
 */
static void MENU_FreeItemData( MENUITEM* item )
{
    /* delete text */
691
    HeapFree( GetProcessHeap(), 0, item->text );
Alexandre Julliard's avatar
Alexandre Julliard committed
692
}
Alexandre Julliard's avatar
Alexandre Julliard committed
693

694 695 696 697 698 699 700 701 702 703
/***********************************************************************
 *           MENU_AdjustMenuItemRect
 *
 * Adjust menu item rectangle according to scrolling state.
 */
static void
MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
{
    if (menu->bScrolling)
    {
704
        UINT arrow_bitmap_height;
705 706 707 708 709 710 711 712 713 714
        BITMAP bmp;

        GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
        arrow_bitmap_height = bmp.bmHeight;
        rect->top += arrow_bitmap_height - menu->nScrollPos;
        rect->bottom += arrow_bitmap_height - menu->nScrollPos;
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
715 716 717
/***********************************************************************
 *           MENU_FindItemByCoords
 *
718 719
 * Find the item at the specified coordinates (screen coords). Does
 * not work for child windows and therefore should not be called for
Alexandre Julliard's avatar
Alexandre Julliard committed
720
 * an arbitrary system menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
721
 */
722
static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
723
					POINT pt, UINT *pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
724 725
{
    MENUITEM *item;
726
    UINT i;
727
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
728

729 730 731
    if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
    pt.x -= rect.left;
    pt.y -= rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
732
    item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
733 734
    for (i = 0; i < menu->nItems; i++, item++)
    {
735 736
        rect = item->rect;
        MENU_AdjustMenuItemRect(menu, &rect);
737
	if (PtInRect(&rect, pt))
Alexandre Julliard's avatar
Alexandre Julliard committed
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
	{
	    if (pos) *pos = i;
	    return item;
	}
    }
    return NULL;
}


/***********************************************************************
 *           MENU_FindItemByKey
 *
 * Find the menu item selected by a key press.
 * Return item id, -1 if none, -2 if we should close the menu.
 */
753
static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
754
                                WCHAR key, BOOL forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
755
{
756
    TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
757

758
    if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
759

Alexandre Julliard's avatar
Alexandre Julliard committed
760
    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
761
    {
762
	POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
763
	MENUITEM *item = menu->items;
764
	LRESULT menuchar;
Alexandre Julliard's avatar
Alexandre Julliard committed
765 766

	if( !forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
767
	{
768
	     UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
769 770 771

	     for (i = 0; i < menu->nItems; i++, item++)
	     {
772
		if( item->text)
Alexandre Julliard's avatar
Alexandre Julliard committed
773
		{
774
		    WCHAR *p = item->text - 2;
775 776
		    do
		    {
777
		    	p = strchrW (p + 2, '&');
778 779
		    }
		    while (p != NULL && p [1] == '&');
780
		    if (p && (toupperW(p[1]) == toupperW(key))) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
781 782
		}
	     }
Alexandre Julliard's avatar
Alexandre Julliard committed
783
	}
784
	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
785
                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
786
	if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
787
	if (HIWORD(menuchar) == 1) return (UINT)(-2);
Alexandre Julliard's avatar
Alexandre Julliard committed
788
    }
789
    return (UINT)(-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
790
}
791 792


793
/***********************************************************************
794
 *           MENU_GetBitmapItemSize
795
 *
796
 * Get the size of a bitmap item.
797
 */
Rein Klazes's avatar
Rein Klazes committed
798 799
static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
                                    HWND hwndOwner)
800 801
{
    BITMAP bm;
Rein Klazes's avatar
Rein Klazes committed
802
    HBITMAP bmp = lpitem->hbmpItem;
803 804

    size->cx = size->cy = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
805

806
    /* check if there is a magic menu item associated with this item */
Rein Klazes's avatar
Rein Klazes committed
807
    switch( (INT_PTR) bmp )
808
    {
Rein Klazes's avatar
Rein Klazes committed
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
    case (INT_PTR)HBMMENU_CALLBACK:
        {
            MEASUREITEMSTRUCT measItem;
            measItem.CtlType = ODT_MENU;
            measItem.CtlID = 0;
            measItem.itemID = lpitem->wID;
            measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
            measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
            measItem.itemData = lpitem->dwItemData;
            SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
            size->cx = measItem.itemWidth;
            size->cy = measItem.itemHeight;
            return;
        }
        break;
824
    case (INT_PTR)HBMMENU_SYSTEM:
Rein Klazes's avatar
Rein Klazes committed
825
        if (lpitem->dwItemData)
826
        {
Rein Klazes's avatar
Rein Klazes committed
827
            bmp = (HBITMAP)lpitem->dwItemData;
828
            break;
829
        }
830 831 832 833 834 835 836 837 838 839 840 841 842
        /* fall through */
    case (INT_PTR)HBMMENU_MBAR_RESTORE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
    case (INT_PTR)HBMMENU_MBAR_CLOSE:
    case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
        size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
        size->cy = size->cx;
        return;
    case (INT_PTR)HBMMENU_POPUP_CLOSE:
    case (INT_PTR)HBMMENU_POPUP_RESTORE:
    case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
    case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
843 844
        size->cx = GetSystemMetrics( SM_CXMENUSIZE);
        size->cy = GetSystemMetrics( SM_CYMENUSIZE);
845
        return;
846
    }
847
    if (GetObjectW(bmp, sizeof(bm), &bm ))
848 849 850 851 852 853 854 855 856 857 858
    {
        size->cx = bm.bmWidth;
        size->cy = bm.bmHeight;
    }
}

/***********************************************************************
 *           MENU_DrawBitmapItem
 *
 * Draw a bitmap item.
 */
Rein Klazes's avatar
Rein Klazes committed
859 860
static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
                    HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
861
{
862 863 864
    BITMAP bm;
    DWORD rop;
    HDC hdcMem;
865
    HBITMAP bmp;
866 867 868 869
    int w = rect->right - rect->left;
    int h = rect->bottom - rect->top;
    int bmp_xoffset = 0;
    int left, top;
870 871
    HBITMAP hbmToDraw = lpitem->hbmpItem;
    bmp = hbmToDraw;
872 873

    /* Check if there is a magic menu item associated with this item */
874
    if (IS_MAGIC_BITMAP(hbmToDraw))
875 876
    {
        UINT flags = 0;
877
        WCHAR bmchr = 0;
878 879
        RECT r;

880
	switch((INT_PTR)hbmToDraw)
881
        {
882
        case (INT_PTR)HBMMENU_SYSTEM:
883 884 885
            if (lpitem->dwItemData)
            {
                bmp = (HBITMAP)lpitem->dwItemData;
886
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
887 888 889
            }
            else
            {
890 891 892
                static HBITMAP hBmpSysMenu;

                if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
893
                bmp = hBmpSysMenu;
894
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
895 896 897 898 899
                /* only use right half of the bitmap */
                bmp_xoffset = bm.bmWidth / 2;
                bm.bmWidth -= bmp_xoffset;
            }
            goto got_bitmap;
900
        case (INT_PTR)HBMMENU_MBAR_RESTORE:
901 902
            flags = DFCS_CAPTIONRESTORE;
            break;
903
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
904 905
            flags = DFCS_CAPTIONMIN;
            break;
906
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
907 908
            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
            break;
909
        case (INT_PTR)HBMMENU_MBAR_CLOSE:
910 911
            flags = DFCS_CAPTIONCLOSE;
            break;
912
        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
913 914
            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
            break;
915
        case (INT_PTR)HBMMENU_CALLBACK:
Rein Klazes's avatar
Rein Klazes committed
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
            {
                DRAWITEMSTRUCT drawItem;
                drawItem.CtlType = ODT_MENU;
                drawItem.CtlID = 0;
                drawItem.itemID = lpitem->wID;
                drawItem.itemAction = odaction;
                drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
                drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
                drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
                drawItem.hwndItem = (HWND)hmenu;
                drawItem.hDC = hdc;
                drawItem.itemData = lpitem->dwItemData;
                drawItem.rcItem = *rect;
                SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
                return;
            }
            break;
935
        case (INT_PTR)HBMMENU_POPUP_CLOSE:
936 937
            bmchr = 0x72;
            break;
938
        case (INT_PTR)HBMMENU_POPUP_RESTORE:
939 940
            bmchr = 0x32;
            break;
941
        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
942 943
            bmchr = 0x31;
            break;
944
        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
945 946
            bmchr = 0x30;
            break;
947
        default:
948
            FIXME("Magic %p not implemented\n", hbmToDraw);
949 950
            return;
        }
951 952
        if (bmchr)
        {
953 954
            /* draw the magic bitmaps using marlett font characters */
            /* FIXME: fontsize and the position (x,y) could probably be better */
955 956 957 958
            HFONT hfont, hfontsav;
            LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
                0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
                { 'M','a','r','l','e','t','t',0 } };
959
            logfont.lfHeight =  min( h, w) - 5 ;
960 961 962
            TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
            hfont = CreateFontIndirectW( &logfont);
            hfontsav = SelectObject(hdc, hfont);
963
            TextOutW( hdc,  rect->left, rect->top + 2, &bmchr, 1);
964 965 966 967 968 969 970 971 972 973
            SelectObject(hdc, hfontsav);
            DeleteObject( hfont);
        }
        else
        {
            r = *rect;
            InflateRect( &r, -1, -1 );
            if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
            DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
        }
974
        return;
975 976
    }

977
    if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
978 979 980 981 982 983 984 985

 got_bitmap:
    hdcMem = CreateCompatibleDC( hdc );
    SelectObject( hdcMem, bmp );

    /* handle fontsize > bitmap_height */
    top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
    left=rect->left;
986 987
    rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
    if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
988
        SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
989 990
    BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
    DeleteDC( hdcMem );
991
}
Alexandre Julliard's avatar
Alexandre Julliard committed
992

993

Alexandre Julliard's avatar
Alexandre Julliard committed
994 995 996 997 998
/***********************************************************************
 *           MENU_CalcItemSize
 *
 * Calculate the size of the menu item and store it in lpitem->rect.
 */
999
static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1000
			       INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
Alexandre Julliard's avatar
Alexandre Julliard committed
1001
{
1002
    WCHAR *p;
1003
    UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1004 1005
    UINT arrow_bitmap_width;
    BITMAP bm;
1006
    INT itemheight;
Alexandre Julliard's avatar
Alexandre Julliard committed
1007

1008
    TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1009
    debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
Alexandre Julliard's avatar
Alexandre Julliard committed
1010
			 (menuBar ? " (MenuBar)" : ""));
Alexandre Julliard's avatar
Alexandre Julliard committed
1011

1012 1013 1014
    GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
    arrow_bitmap_width = bm.bmWidth;

Rein Klazes's avatar
Rein Klazes committed
1015 1016 1017 1018 1019 1020 1021 1022
    /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
    if( !menucharsize.cx ) {
        menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
        /* Win95/98/ME will use menucharsize.cy here. Testing is possible
         * but it is unlikely an application will depend on that */
        ODitemheight = HIWORD( GetDialogBaseUnits());
    }

1023
    SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1024

Alexandre Julliard's avatar
Alexandre Julliard committed
1025
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1026
    {
1027
        MEASUREITEMSTRUCT mis;
Alexandre Julliard's avatar
Alexandre Julliard committed
1028
        mis.CtlType    = ODT_MENU;
1029
        mis.CtlID      = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1030
        mis.itemID     = lpitem->wID;
1031
        mis.itemData   = lpitem->dwItemData;
1032
        mis.itemHeight = ODitemheight;
1033
        mis.itemWidth  = 0;
1034
        SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1035 1036 1037 1038 1039 1040 1041 1042 1043
        /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
         * width of a menufont character to the width of an owner-drawn menu. 
         */
        lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
        if (menuBar) {
            /* under at least win95 you seem to be given a standard
               height for the menu and the height value is ignored */
            lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
        } else
1044
            lpitem->rect.bottom += mis.itemHeight;
1045

1046
        TRACE("id=%04lx size=%dx%d\n",
1047 1048 1049
                lpitem->wID, lpitem->rect.right-lpitem->rect.left,
                lpitem->rect.bottom-lpitem->rect.top);
        return;
1050
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1051

Alexandre Julliard's avatar
Alexandre Julliard committed
1052
    if (lpitem->fType & MF_SEPARATOR)
Alexandre Julliard's avatar
Alexandre Julliard committed
1053
    {
Rein Klazes's avatar
Rein Klazes committed
1054 1055 1056 1057
        lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
        if( !menuBar)
            lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1058 1059
    }

1060
    itemheight = 0;
1061
    lpitem->xTab = 0;
1062

Rein Klazes's avatar
Rein Klazes committed
1063 1064 1065
    if (!menuBar) {
        if (lpitem->hbmpItem) {
            SIZE size;
Rein Klazes's avatar
Rein Klazes committed
1066

Rein Klazes's avatar
Rein Klazes committed
1067 1068 1069 1070
            MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
            /* Keep the size of the bitmap in callback mode to be able
             * to draw it correctly */
            lpitem->bmpsize = size;
1071
            lppop->textOffset = max( lppop->textOffset, size.cx);
Rein Klazes's avatar
Rein Klazes committed
1072 1073 1074 1075 1076
            lpitem->rect.right += size.cx + 2;
            itemheight = size.cy + 2;
        }
        if( !(lppop->dwStyle & MNS_NOCHECK))
            lpitem->rect.right += check_bitmap_width; 
1077 1078 1079
        lpitem->rect.right += 4 + menucharsize.cx;
        lpitem->xTab = lpitem->rect.right;
        lpitem->rect.right += arrow_bitmap_width;
Rein Klazes's avatar
Rein Klazes committed
1080
    } else if (lpitem->hbmpItem) { /* menuBar */
1081
        SIZE size;
1082

Rein Klazes's avatar
Rein Klazes committed
1083 1084
        MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
        lpitem->bmpsize = size;
1085
        lpitem->rect.right  += size.cx;
1086 1087
        if( lpitem->text) lpitem->rect.right  += 2;
        itemheight = size.cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
1088
    }
1089

1090
    /* it must be a text item - unless it's the system menu */
1091
    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1092
        HFONT hfontOld = NULL;
1093 1094
        RECT rc = lpitem->rect;
        LONG txtheight, txtwidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
1095

1096 1097 1098
	if ( lpitem->fState & MFS_DEFAULT ) {
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
	}
1099 1100 1101 1102 1103 1104
        if (menuBar) {
            txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                    DT_SINGLELINE|DT_CALCRECT); 
            lpitem->rect.right  += rc.right - rc.left;
            itemheight = max( max( itemheight, txtheight),
                    GetSystemMetrics( SM_CYMENU) - 1);
Rein Klazes's avatar
Rein Klazes committed
1105
            lpitem->rect.right +=  2 * menucharsize.cx;
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
        } else {
            if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
                RECT tmprc = rc;
                LONG tmpheight;
                int n = (int)( p - lpitem->text);
                /* Item contains a tab (only meaningful in popup menus) */
                /* get text size before the tab */
                txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
                p += 1; /* advance past the Tab */
                /* get text size after the tab */
                tmpheight = DrawTextW( hdc, p, -1, &tmprc,
                        DT_SINGLELINE|DT_CALCRECT);
1120
                lpitem->xTab += txtwidth;
1121 1122 1123 1124 1125 1126 1127
                txtheight = max( txtheight, tmpheight);
                txtwidth += menucharsize.cx + /* space for the tab */
                    tmprc.right - tmprc.left; /* space for the short cut */
            } else {
                txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
1128
                lpitem->xTab += txtwidth;
1129 1130 1131 1132 1133
            }
            lpitem->rect.right  += 2 + txtwidth;
            itemheight = max( itemheight,
                    max( txtheight + 2, menucharsize.cy + 4));
        }
1134
	if (hfontOld) SelectObject (hdc, hfontOld);
1135 1136
    } else if( menuBar) {
        itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1137
    }
1138
    lpitem->rect.bottom += itemheight;
1139
    TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
Alexandre Julliard's avatar
Alexandre Julliard committed
1140 1141 1142
}


1143 1144 1145 1146
/***********************************************************************
 *           MENU_GetMaxPopupHeight
 */
static UINT
1147
MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1148 1149 1150 1151 1152 1153 1154
{
    if (lppop->cyMax)
        return lppop->cyMax;
    return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1155 1156 1157 1158 1159
/***********************************************************************
 *           MENU_PopupMenuCalcSize
 *
 * Calculate the size of a popup menu.
 */
1160
static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
Alexandre Julliard's avatar
Alexandre Julliard committed
1161
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1162
    MENUITEM *lpitem;
1163
    HDC hdc;
1164
    UINT start, i;
1165
    int textandbmp = FALSE;
1166
    int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
1167 1168 1169

    lppop->Width = lppop->Height = 0;
    if (lppop->nItems == 0) return;
1170
    hdc = GetDC( 0 );
1171

1172
    SelectObject( hdc, get_menu_font(FALSE));
1173

Alexandre Julliard's avatar
Alexandre Julliard committed
1174
    start = 0;
1175
    maxX = 2 + 1;
1176

1177
    lppop->textOffset = 0;
1178

Alexandre Julliard's avatar
Alexandre Julliard committed
1179
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1180
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1181
	lpitem = &lppop->items[start];
Alexandre Julliard's avatar
Alexandre Julliard committed
1182
	orgX = maxX;
1183
        if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1184
            orgX += MENU_COL_SPACE; 
1185
	orgY = MENU_TOP_MARGIN;
Alexandre Julliard's avatar
Alexandre Julliard committed
1186

Alexandre Julliard's avatar
Alexandre Julliard committed
1187
	maxTab = maxTabWidth = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1188
	  /* Parse items until column break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
1189
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1190
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
1191
	    if ((i != start) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1192
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1193

1194
	    MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1195
	    maxX = max( maxX, lpitem->rect.right );
Alexandre Julliard's avatar
Alexandre Julliard committed
1196
	    orgY = lpitem->rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1197
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
Alexandre Julliard's avatar
Alexandre Julliard committed
1198
	    {
1199 1200
		maxTab = max( maxTab, lpitem->xTab );
		maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
Alexandre Julliard's avatar
Alexandre Julliard committed
1201
	    }
1202
            if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1203 1204 1205
	}

	  /* Finish the column (set all items to the largest width found) */
1206
	maxX = max( maxX, maxTab + maxTabWidth );
Alexandre Julliard's avatar
Alexandre Julliard committed
1207
	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1208 1209
	{
	    lpitem->rect.right = maxX;
Alexandre Julliard's avatar
Alexandre Julliard committed
1210
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1211
		lpitem->xTab = maxTab;
1212

Alexandre Julliard's avatar
Alexandre Julliard committed
1213
	}
1214
	lppop->Height = max( lppop->Height, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1215 1216
    }

1217
    lppop->Width  = maxX;
1218 1219 1220 1221 1222 1223
    /* if none of the items have both text and bitmap then
     * the text and bitmaps are all aligned on the left. If there is at
     * least one item with both text and bitmap then bitmaps are
     * on the left and texts left aligned with the right hand side
     * of the bitmaps */
    if( !textandbmp) lppop->textOffset = 0;
1224 1225

    /* space for 3d border */
1226
    lppop->Height += MENU_BOTTOM_MARGIN;
1227
    lppop->Width += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
1228

1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241
    /* Adjust popup height if it exceeds maximum */
    maxHeight = MENU_GetMaxPopupHeight(lppop);
    lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
    if (lppop->Height >= maxHeight)
    {
        lppop->Height = maxHeight;
        lppop->bScrolling = TRUE;
    }
    else
    {
        lppop->bScrolling = FALSE;
    }

1242
    ReleaseDC( 0, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1243 1244 1245 1246 1247 1248
}


/***********************************************************************
 *           MENU_MenuBarCalcSize
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1249
 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
Alexandre Julliard's avatar
Alexandre Julliard committed
1250 1251 1252
 * height is off by 1 pixel which causes lengthy window relocations when
 * active document window is maximized/restored.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1253 1254
 * Calculate the size of the menu bar.
 */
1255 1256
static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
                                  LPPOPUPMENU lppop, HWND hwndOwner )
Alexandre Julliard's avatar
Alexandre Julliard committed
1257
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1258
    MENUITEM *lpitem;
1259 1260
    UINT start, i, helpPos;
    int orgX, orgY, maxY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1261 1262 1263

    if ((lprect == NULL) || (lppop == NULL)) return;
    if (lppop->nItems == 0) return;
1264
    TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
Alexandre Julliard's avatar
Alexandre Julliard committed
1265 1266
    lppop->Width  = lprect->right - lprect->left;
    lppop->Height = 0;
1267
    maxY = lprect->top+1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1268
    start = 0;
1269
    helpPos = ~0U;
1270
    lppop->textOffset = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1271
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1272
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1273
	lpitem = &lppop->items[start];
Alexandre Julliard's avatar
Alexandre Julliard committed
1274 1275 1276 1277
	orgX = lprect->left;
	orgY = maxY;

	  /* Parse items until line break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
1278
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1279
	{
1280
	    if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1281
	    if ((i != start) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1282
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1283

1284
	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1285
	    debug_print_menuitem ("  item: ", lpitem, "");
1286
	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1287

Alexandre Julliard's avatar
Alexandre Julliard committed
1288 1289
	    if (lpitem->rect.right > lprect->right)
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1290
		if (i != start) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1291 1292
		else lpitem->rect.right = lprect->right;
	    }
1293
	    maxY = max( maxY, lpitem->rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
1294 1295 1296 1297
	    orgX = lpitem->rect.right;
	}

	  /* Finish the line (set all items to the largest height found) */
Alexandre Julliard's avatar
Alexandre Julliard committed
1298
	while (start < i) lppop->items[start++].rect.bottom = maxY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1299 1300 1301 1302
    }

    lprect->bottom = maxY;
    lppop->Height = lprect->bottom - lprect->top;
Alexandre Julliard's avatar
Alexandre Julliard committed
1303

1304 1305
    /* Flush right all items between the MF_RIGHTJUSTIFY and */
    /* the last item (if several lines, only move the last line) */
1306
    if (helpPos == ~0U) return;
1307 1308 1309 1310 1311 1312 1313 1314 1315
    lpitem = &lppop->items[lppop->nItems-1];
    orgY = lpitem->rect.top;
    orgX = lprect->right;
    for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
        if (lpitem->rect.top != orgY) break;	/* Other line */
        if (lpitem->rect.right >= orgX) break;	/* Too far right already */
        lpitem->rect.left += orgX - lpitem->rect.right;
        lpitem->rect.right = orgX;
        orgX = lpitem->rect.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
1316
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1317 1318
}

1319 1320 1321 1322 1323 1324 1325

/***********************************************************************
 *           MENU_DrawScrollArrows
 *
 * Draw scroll arrows.
 */
static void
1326
MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
{
    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP hOrigBitmap;
    UINT arrow_bitmap_width, arrow_bitmap_height;
    BITMAP bmp;
    RECT rect;

    GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
    arrow_bitmap_width = bmp.bmWidth;
    arrow_bitmap_height = bmp.bmHeight;

    
    if (lppop->nScrollPos)
        hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
    else
        hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
    rect.left = 0;
    rect.top = 0;
    rect.right = lppop->Width;
    rect.bottom = arrow_bitmap_height;
    FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
    BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
           arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
    rect.top = lppop->Height - arrow_bitmap_height;
    rect.bottom = lppop->Height;
    FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
    if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
        SelectObject(hdcMem, get_down_arrow_bitmap());
    else
        SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
    BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
           lppop->Height - arrow_bitmap_height,
           arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, hOrigBitmap);
    DeleteDC(hdcMem);
}


1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383
/***********************************************************************
 *           draw_popup_arrow
 *
 * Draws the popup-menu arrow.
 */
static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
        UINT arrow_bitmap_height)
{
    HDC hdcMem = CreateCompatibleDC( hdc );
    HBITMAP hOrigBitmap;

    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
            (rect.top + rect.bottom - arrow_bitmap_height) / 2,
            arrow_bitmap_width, arrow_bitmap_height,
            hdcMem, 0, 0, SRCCOPY );
    SelectObject( hdcMem, hOrigBitmap );
    DeleteDC( hdcMem );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1384 1385 1386 1387 1388
/***********************************************************************
 *           MENU_DrawMenuItem
 *
 * Draw a single menu item.
 */
1389
static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1390
			       UINT height, BOOL menuBar, UINT odaction )
Alexandre Julliard's avatar
Alexandre Julliard committed
1391
{
1392
    RECT rect;
1393 1394
    BOOL flat_menu = FALSE;
    int bkgnd;
1395
    UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
Rein Klazes's avatar
Rein Klazes committed
1396 1397
    POPUPMENU *menu = MENU_GetMenu(hmenu);
    RECT bmprc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1398

Alexandre Julliard's avatar
Alexandre Julliard committed
1399
    debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
1400

1401 1402 1403 1404 1405 1406 1407
    if (!menuBar) {
        BITMAP bmp;
        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
        arrow_bitmap_width = bmp.bmWidth;
        arrow_bitmap_height = bmp.bmHeight;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1408
    if (lpitem->fType & MF_SYSMENU)
Alexandre Julliard's avatar
Alexandre Julliard committed
1409
    {
1410 1411
	if( !IsIconic(hwnd) )
	    NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1412 1413 1414
	return;
    }

1415 1416 1417
    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
    bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
  
1418 1419 1420 1421
      /* Setup colors */

    if (lpitem->fState & MF_HILITE)
    {
1422
        if(menuBar && !flat_menu) {
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
	    SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_MENU));
	} else {
	    if(lpitem->fState & MF_GRAYED)
		SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
	    else
		SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
	}
    }
    else
    {
	if (lpitem->fState & MF_GRAYED)
	    SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
	else
	    SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1439
	SetBkColor( hdc, GetSysColor( bkgnd ) );
1440 1441
    }

1442 1443 1444 1445
    TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
    rect = lpitem->rect;
    MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);

Alexandre Julliard's avatar
Alexandre Julliard committed
1446
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1447
    {
1448 1449 1450 1451 1452 1453 1454 1455 1456
        /*
        ** Experimentation under Windows reveals that an owner-drawn
        ** menu is given the rectangle which includes the space it requested
        ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
        ** and a popup-menu arrow.  This is the value of lpitem->rect.
        ** Windows will leave all drawing to the application except for
        ** the popup-menu arrow.  Windows always draws that itself, after
        ** the menu owner has finished drawing.
        */
1457
        DRAWITEMSTRUCT dis;
Alexandre Julliard's avatar
Alexandre Julliard committed
1458

Alexandre Julliard's avatar
Alexandre Julliard committed
1459
        dis.CtlType   = ODT_MENU;
1460
	dis.CtlID     = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1461
        dis.itemID    = lpitem->wID;
1462
        dis.itemData  = lpitem->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
1463
        dis.itemState = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1464
        if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1465
        if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1466
        if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1467
        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1468
        dis.hwndItem   = (HWND)hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1469
        dis.hDC        = hdc;
1470
        dis.rcItem     = rect;
1471
        TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1472
	      "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1473
	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1474
	      dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1475
        SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1476 1477 1478 1479 1480
        /* Draw the popup-menu arrow */
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1481
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1482

1483
    if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1484

1485
    if (lpitem->fState & MF_HILITE)
1486
    {
1487 1488 1489 1490 1491 1492 1493
        if (flat_menu)
        {
            InflateRect (&rect, -1, -1);
            FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
            InflateRect (&rect, 1, 1);
            FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
1494
        else
1495 1496 1497 1498 1499 1500
        {
            if(menuBar)
                DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
            else
                FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
1501
    }
1502 1503
    else
        FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1504

1505
    SetBkMode( hdc, TRANSPARENT );
Alexandre Julliard's avatar
Alexandre Julliard committed
1506

1507 1508
    /* vertical separator */
    if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
1509
    {
1510 1511 1512
        HPEN oldPen;
        RECT rc = rect;

1513
        rc.left -= MENU_COL_SPACE / 2 + 1;
1514 1515 1516
        rc.top = 3;
        rc.bottom = height - 3;
        if (flat_menu)
1517
        {
1518 1519 1520 1521
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.left, rc.bottom );
            SelectObject( hdc, oldPen );
1522
        }
1523 1524 1525
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
    }
1526

1527 1528 1529 1530 1531 1532 1533 1534
    /* horizontal separator */
    if (lpitem->fType & MF_SEPARATOR)
    {
        HPEN oldPen;
        RECT rc = rect;

        rc.left++;
        rc.right--;
Rein Klazes's avatar
Rein Klazes committed
1535
        rc.top = ( rc.top + rc.bottom) / 2;
1536
        if (flat_menu)
1537
        {
1538 1539 1540 1541
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.right, rc.top );
            SelectObject( hdc, oldPen );
1542
        }
1543 1544 1545
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1546 1547
    }

1548 1549
	/* helper lines for debugging */
/*	FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1550
	SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1551
	MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1552 1553 1554
	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
*/

Rein Klazes's avatar
Rein Klazes committed
1555 1556 1557 1558 1559 1560 1561 1562 1563
    if (lpitem->hbmpItem) {
        /* calculate the bitmap rectangle in coordinates relative
         * to the item rectangle */
        if( menuBar) {
            if( lpitem->hbmpItem == HBMMENU_CALLBACK)
                bmprc.left = 3;
            else 
                bmprc.left = lpitem->text ? menucharsize.cx : 0;          
        }
1564 1565 1566 1567 1568 1569
        else if (menu->dwStyle & MNS_NOCHECK)
            bmprc.left = 4;
        else if (menu->dwStyle & MNS_CHECKORBMP)
            bmprc.left = 2;
        else
            bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
Rein Klazes's avatar
Rein Klazes committed
1570 1571 1572 1573
        bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
        if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
            bmprc.top = 0;
        else
1574
            bmprc.top = (rect.bottom - rect.top -
Rein Klazes's avatar
Rein Klazes committed
1575 1576 1577 1578
                    lpitem->bmpsize.cy) / 2; 
        bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1579 1580
    if (!menuBar)
    {
1581 1582
        HBITMAP bm;
        INT y = rect.top + rect.bottom;
1583
        RECT rc = rect;
Rein Klazes's avatar
Rein Klazes committed
1584
        int checked = FALSE;
1585 1586
        UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
        UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1587 1588 1589 1590 1591
        /* Draw the check mark
         *
         * FIXME:
         * Custom checkmark bitmaps are monochrome but not always 1bpp.
         */
Rein Klazes's avatar
Rein Klazes committed
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623
        if( !(menu->dwStyle & MNS_NOCHECK)) {
            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
                lpitem->hUnCheckBit;
            if (bm)  /* we have a custom bitmap */
            {
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
                BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
                        check_bitmap_width, check_bitmap_height,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                checked = TRUE;
            }
            else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
            {
                RECT r;
                HBITMAP bm = CreateBitmap( check_bitmap_width,
                        check_bitmap_height, 1, 1, NULL );
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
                DrawFrameControl( hdcMem, &r, DFC_MENU,
                        (lpitem->fType & MFT_RADIOCHECK) ?
                        DFCS_MENUBULLET : DFCS_MENUCHECK );
                BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                DeleteObject( bm );
                checked = TRUE;
            }
1624
        }
Rein Klazes's avatar
Rein Klazes committed
1625 1626
        if( lpitem->hbmpItem &&
                !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
Rein Klazes's avatar
Rein Klazes committed
1627 1628
            POINT origorg;
            /* some applications make this assumption on the DC's origin */
1629
            SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
Rein Klazes's avatar
Rein Klazes committed
1630 1631 1632
            MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
                    odaction, FALSE);
            SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1633
        }
1634 1635 1636 1637
	/* Draw the popup-menu arrow */
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
Rein Klazes's avatar
Rein Klazes committed
1638 1639 1640
	rect.left += 4;
        if( !(menu->dwStyle & MNS_NOCHECK))
            rect.left += check_bitmap_width;
Alexandre Julliard's avatar
Alexandre Julliard committed
1641 1642
	rect.right -= arrow_bitmap_width;
    }
1643
    else if( lpitem->hbmpItem)
1644
    {   /* Draw the bitmap */
Rein Klazes's avatar
Rein Klazes committed
1645 1646
        POINT origorg;
        
1647
        SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
Rein Klazes's avatar
Rein Klazes committed
1648 1649 1650
        MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
                odaction, menuBar);
        SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1651 1652 1653
    }
    /* process text if present */
    if (lpitem->text)
Alexandre Julliard's avatar
Alexandre Julliard committed
1654
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1655
	register int i;
1656
	HFONT hfontOld = 0;
1657

1658 1659 1660
	UINT uFormat = (menuBar) ?
			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
			DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1661

Rein Klazes's avatar
Rein Klazes committed
1662
        if( !(menu->dwStyle & MNS_CHECKORBMP))
1663
            rect.left += menu->textOffset;
Rein Klazes's avatar
Rein Klazes committed
1664

1665 1666
	if ( lpitem->fState & MFS_DEFAULT )
	{
1667
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1668 1669
	}

Rein Klazes's avatar
Rein Klazes committed
1670 1671 1672 1673 1674 1675
	if (menuBar) {
            if( lpitem->hbmpItem)
                rect.left += lpitem->bmpsize.cx;
            if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
                rect.left += menucharsize.cx;
            rect.right -= menucharsize.cx;
Alexandre Julliard's avatar
Alexandre Julliard committed
1676 1677
	}

1678 1679 1680 1681
	for (i = 0; lpitem->text[i]; i++)
	    if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
	        break;

1682
	if(lpitem->fState & MF_GRAYED)
1683
	{
1684
	    if (!(lpitem->fState & MF_HILITE) )
1685
	    {
1686
		++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1687
		SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1688
		DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1689
		--rect.left; --rect.top; --rect.right; --rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1690
	    }
1691
	    SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
Alexandre Julliard's avatar
Alexandre Julliard committed
1692
	}
1693

1694
	DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
1695

1696
	/* paint the shortcut text */
1697
	if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
Alexandre Julliard's avatar
Alexandre Julliard committed
1698
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
1699
	    if (lpitem->text[i] == '\t')
Alexandre Julliard's avatar
Alexandre Julliard committed
1700 1701
	    {
		rect.left = lpitem->xTab;
1702
		uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1703
	    }
1704
	    else
1705
	    {
1706
		rect.right = lpitem->xTab;
1707 1708 1709
		uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
	    }

1710
	    if(lpitem->fState & MF_GRAYED)
1711 1712 1713 1714 1715
	    {
		if (!(lpitem->fState & MF_HILITE) )
		{
		    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
		    SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1716
		    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1717 1718 1719 1720
		    --rect.left; --rect.top; --rect.right; --rect.bottom;
		}
		SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
	    }
1721
	    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1722
	}
1723

1724
	if (hfontOld)
1725
	    SelectObject (hdc, hfontOld);
Alexandre Julliard's avatar
Alexandre Julliard committed
1726 1727 1728 1729
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1730
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1731 1732 1733
 *           MENU_DrawPopupMenu
 *
 * Paint a popup menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
1734
 */
1735
static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1736
{
1737 1738
    HBRUSH hPrevBrush = 0;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1739

1740
    TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1741

1742
    GetClientRect( hwnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1743

1744
    if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1745
        && (SelectObject( hdc, get_menu_font(FALSE))))
Alexandre Julliard's avatar
Alexandre Julliard committed
1746
    {
1747
	HPEN hPrevPen;
1748

1749
	Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
1750

1751
	hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1752 1753 1754
	if( hPrevPen )
	{
	    POPUPMENU *menu;
1755
	    BOOL flat_menu = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1756

1757 1758 1759 1760 1761
	    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
	    if (flat_menu)
		FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
	    else
		DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1762

1763 1764
            if( (menu = MENU_GetMenu( hmenu )))
            {
1765
                TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780
                /* draw menu items */
                if( menu->nItems)
                {
                    MENUITEM *item;
                    UINT u;

                    item = menu->items;
                    for( u = menu->nItems; u > 0; u--, item++)
                        MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
                                item, menu->Height, FALSE, ODA_DRAWENTIRE );
                }
                /* draw scroll arrows */
                if (menu->bScrolling)
                    MENU_DrawScrollArrows(menu, hdc);
            }
1781
 	} else
1782 1783 1784
	{
	    SelectObject( hdc, hPrevBrush );
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1785
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1786
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1787

Alexandre Julliard's avatar
Alexandre Julliard committed
1788 1789 1790 1791
/***********************************************************************
 *           MENU_DrawMenuBar
 *
 * Paint a menu bar. Returns the height of the menu bar.
1792
 * called from [windows/nonclient.c]
Alexandre Julliard's avatar
Alexandre Julliard committed
1793
 */
1794 1795
UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
                         BOOL suppress_draw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1796 1797
{
    LPPOPUPMENU lppop;
1798
    HFONT hfontOld = 0;
1799
    HMENU hMenu = GetMenu(hwnd);
1800

1801
    lppop = MENU_GetMenu( hMenu );
1802 1803
    if (lppop == NULL || lprect == NULL)
    {
1804
        return GetSystemMetrics(SM_CYMENU);
1805
    }
1806

1807 1808
    if (suppress_draw)
    {
1809
	hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1810

1811 1812
	if (lppop->Height == 0)
		MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1813

1814
	lprect->bottom = lprect->top + lppop->Height;
1815

1816 1817
        if (hfontOld) SelectObject( hDC, hfontOld);
	return lppop->Height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1818
    }
1819 1820
    else
        return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1821
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1822

1823

Alexandre Julliard's avatar
Alexandre Julliard committed
1824 1825 1826 1827 1828
/***********************************************************************
 *           MENU_ShowPopup
 *
 * Display a popup menu.
 */
1829
static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1830
                              INT x, INT y, INT xanchor, INT yanchor )
Alexandre Julliard's avatar
Alexandre Julliard committed
1831
{
1832
    POPUPMENU *menu;
1833
    INT width, height;
1834 1835 1836
    POINT pt;
    HMONITOR monitor;
    MONITORINFO info;
Alexandre Julliard's avatar
Alexandre Julliard committed
1837

1838 1839
    TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
          hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1840

1841
    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1842 1843
    if (menu->FocusedItem != NO_SELECTED_ITEM)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1844
	menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1845 1846
	menu->FocusedItem = NO_SELECTED_ITEM;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1847

1848
    /* store the owner for DrawItem */
1849 1850
    menu->hwndOwner = hwndOwner;

1851
    menu->nScrollPos = 0;
1852
    MENU_PopupMenuCalcSize( menu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1853

1854
    /* adjust popup menu pos so that it fits within the desktop */
Alexandre Julliard's avatar
Alexandre Julliard committed
1855

1856
    width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1857
    height = menu->Height + GetSystemMetrics(SM_CYBORDER);
Alexandre Julliard's avatar
Alexandre Julliard committed
1858

1859 1860 1861 1862 1863 1864
    /* FIXME: should use item rect */
    pt.x = x;
    pt.y = y;
    monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
    info.cbSize = sizeof(info);
    GetMonitorInfoW( monitor, &info );
1865 1866 1867 1868 1869 1870 1871

    if( flags & TPM_RIGHTALIGN ) x -= width;
    if( flags & TPM_CENTERALIGN ) x -= width / 2;

    if( flags & TPM_BOTTOMALIGN ) y -= height;
    if( flags & TPM_VCENTERALIGN ) y -= height / 2;

1872
    if( x + width > info.rcWork.right)
1873
    {
1874
        if( xanchor && x >= width - xanchor )
1875
            x -= width - xanchor;
1876

1877 1878
        if( x + width > info.rcWork.right)
            x = info.rcWork.right - width;
1879
    }
1880
    if( x < info.rcWork.left ) x = info.rcWork.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
1881

1882
    if( y + height > info.rcWork.bottom)
1883
    {
1884
        if( yanchor && y >= height + yanchor )
1885
            y -= height + yanchor;
1886

1887 1888
        if( y + height > info.rcWork.bottom)
            y = info.rcWork.bottom - height;
1889
    }
1890
    if( y < info.rcWork.top ) y = info.rcWork.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
1891

1892
    /* NOTE: In Windows, top menu popup is not owned. */
1893
    menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1894
                                WS_POPUP, x, y, width, height,
1895
                                hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1896 1897
                                (LPVOID)hmenu );
    if( !menu->hWnd ) return FALSE;
1898 1899 1900 1901
    if (!top_popup) {
        top_popup = menu->hWnd;
        top_popup_hmenu = hmenu;
    }
1902 1903
    /* Display the window */

1904
    SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1905 1906 1907
                  SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
    UpdateWindow( menu->hWnd );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1908 1909 1910
}


1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951
/***********************************************************************
 *           MENU_EnsureMenuItemVisible
 */
static void
MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
{
    if (lppop->bScrolling)
    {
        MENUITEM *item = &lppop->items[wIndex];
        UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
        UINT nOldPos = lppop->nScrollPos;
        RECT rc;
        UINT arrow_bitmap_height;
        BITMAP bmp;
        
        GetClientRect(lppop->hWnd, &rc);

        GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
        arrow_bitmap_height = bmp.bmHeight;

        rc.top += arrow_bitmap_height;
        rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
       
        nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
        if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
        {
            
            lppop->nScrollPos = item->rect.bottom - nMaxHeight;
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
            MENU_DrawScrollArrows(lppop, hdc);
        }
        else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
        {
            lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
            MENU_DrawScrollArrows(lppop, hdc);
        }
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1952 1953 1954
/***********************************************************************
 *           MENU_SelectItem
 */
1955
static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1956
                             BOOL sendMenuSelect, HMENU topmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1957
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1958
    LPPOPUPMENU lppop;
1959
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1960

1961
    TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1962

1963
    lppop = MENU_GetMenu( hmenu );
1964
    if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1965

Alexandre Julliard's avatar
Alexandre Julliard committed
1966
    if (lppop->FocusedItem == wIndex) return;
1967 1968
    if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
    else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1969 1970 1971 1972
    if (!top_popup) {
        top_popup = lppop->hWnd;
        top_popup_hmenu = hmenu;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1973

1974
    SelectObject( hdc, get_menu_font(FALSE));
1975

Alexandre Julliard's avatar
Alexandre Julliard committed
1976
      /* Clear previous highlighted item */
1977
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
1978
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1979
	lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1980
	MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
Alexandre Julliard's avatar
Alexandre Julliard committed
1981 1982
                          lppop->Height, !(lppop->wFlags & MF_POPUP),
			  ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
1983 1984 1985 1986
    }

      /* Highlight new item (if any) */
    lppop->FocusedItem = wIndex;
1987
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
1988
    {
1989 1990
        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
            lppop->items[wIndex].fState |= MF_HILITE;
1991
            MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1992
            MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1993 1994 1995
                    &lppop->items[wIndex], lppop->Height,
                    !(lppop->wFlags & MF_POPUP), ODA_SELECT );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1996
        if (sendMenuSelect)
Alexandre Julliard's avatar
Alexandre Julliard committed
1997 1998
        {
            MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1999
	    SendMessageW( hwndOwner, WM_MENUSELECT,
2000
                     MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2001
                     ip->fType | ip->fState |
2002
                     (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
2003
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2004
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2005
    else if (sendMenuSelect) {
2006 2007 2008
        if(topmenu){
            int pos;
            if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2009
                POPUPMENU *ptm = MENU_GetMenu( topmenu );
2010
                MENUITEM *ip = &ptm->items[pos];
2011
                SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2012
                         ip->fType | ip->fState |
2013
                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2014 2015
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2016
    }
2017
    ReleaseDC( lppop->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
2018 2019 2020 2021
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2022
 *           MENU_MoveSelection
Alexandre Julliard's avatar
Alexandre Julliard committed
2023
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2024 2025 2026
 * Moves currently selected item according to the offset parameter.
 * If there is no selection then it should select the last item if
 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
Alexandre Julliard's avatar
Alexandre Julliard committed
2027
 */
2028
static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
Alexandre Julliard's avatar
Alexandre Julliard committed
2029
{
2030
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
2031 2032
    POPUPMENU *menu;

2033
    TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2034

2035 2036
    menu = MENU_GetMenu( hmenu );
    if ((!menu) || (!menu->items)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2037 2038

    if ( menu->FocusedItem != NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
2039
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2040
	if( menu->nItems == 1 ) return; else
2041
	for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
Alexandre Julliard's avatar
Alexandre Julliard committed
2042
					    ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
2043
	    if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2044
	    {
2045
		MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2046
		return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2047
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2048
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2049

2050
    for ( i = (offset > 0) ? 0 : menu->nItems - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2051
		  i >= 0 && i < menu->nItems ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
2052
	if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2053
	{
2054
	    MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2055 2056
	    return;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2057 2058 2059
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2060 2061 2062
/**********************************************************************
 *         MENU_InsertItem
 *
2063
 * Insert (allocate) a new item into a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2064
 */
2065
static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2066 2067 2068 2069
{
    MENUITEM *newItems;
    POPUPMENU *menu;

2070
    if (!(menu = MENU_GetMenu(hMenu)))
Alexandre Julliard's avatar
Alexandre Julliard committed
2071 2072 2073 2074
        return NULL;

    /* Find where to insert new item */

2075
    if (flags & MF_BYPOSITION) {
2076
        if (pos > menu->nItems)
2077
            pos = menu->nItems;
2078
    } else {
2079
        if (!MENU_FindItem( &hMenu, &pos, flags ))
2080 2081 2082 2083
            pos = menu->nItems;
        else {
            if (!(menu = MENU_GetMenu( hMenu )))
                return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
2084 2085 2086
        }
    }

2087 2088 2089 2090
    /* Make sure that MDI system buttons stay on the right side.
     * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
     * regardless of their id.
     */
2091
    while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2092 2093 2094 2095 2096
           (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
        pos--;

    TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);

Alexandre Julliard's avatar
Alexandre Julliard committed
2097 2098
    /* Create new items array */

2099
    newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2100
    if (!newItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
2101
    {
2102
        WARN("allocation failed\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
2103 2104 2105 2106
        return NULL;
    }
    if (menu->nItems > 0)
    {
2107
	  /* Copy the old array into the new one */
Alexandre Julliard's avatar
Alexandre Julliard committed
2108 2109
	if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
	if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
Alexandre Julliard's avatar
Alexandre Julliard committed
2110
					(menu->nItems-pos)*sizeof(MENUITEM) );
2111
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
2112
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2113
    menu->items = newItems;
Alexandre Julliard's avatar
Alexandre Julliard committed
2114
    menu->nItems++;
Alexandre Julliard's avatar
Alexandre Julliard committed
2115
    memset( &newItems[pos], 0, sizeof(*newItems) );
2116
    menu->Height = 0; /* force size recalculate */
Alexandre Julliard's avatar
Alexandre Julliard committed
2117 2118 2119 2120 2121 2122 2123
    return &newItems[pos];
}


/**********************************************************************
 *         MENU_ParseResource
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2124
 * Parse a standard menu resource and add items to the menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2125
 * Return a pointer to the end of the resource.
2126 2127
 *
 * NOTE: flags is equivalent to the mtOption field
Alexandre Julliard's avatar
Alexandre Julliard committed
2128
 */
2129
static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2130 2131
{
    WORD flags, id = 0;
2132
    LPCWSTR str;
2133
    BOOL end_flag;
Alexandre Julliard's avatar
Alexandre Julliard committed
2134 2135 2136

    do
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2137
        flags = GET_WORD(res);
2138 2139 2140
        end_flag = flags & MF_END;
        /* Remove MF_END because it has the same value as MF_HILITE */
        flags &= ~MF_END;
Alexandre Julliard's avatar
Alexandre Julliard committed
2141 2142 2143
        res += sizeof(WORD);
        if (!(flags & MF_POPUP))
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
2144
            id = GET_WORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2145 2146
            res += sizeof(WORD);
        }
2147 2148
        str = (LPCWSTR)res;
        res += (strlenW(str) + 1) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
2149 2150
        if (flags & MF_POPUP)
        {
2151
            HMENU hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
2152
            if (!hSubMenu) return NULL;
2153 2154
            if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
            AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
2155
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2156
        else  /* Not a popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
2157
        {
2158
            AppendMenuW( hMenu, flags, id, *str ? str : NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2159
        }
2160
    } while (!end_flag);
Alexandre Julliard's avatar
Alexandre Julliard committed
2161 2162 2163 2164
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2165 2166 2167 2168 2169 2170
/**********************************************************************
 *         MENUEX_ParseResource
 *
 * Parse an extended menu resource and add items to the menu.
 * Return a pointer to the end of the resource.
 */
2171
static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2172 2173
{
    WORD resinfo;
Alexandre Julliard's avatar
Alexandre Julliard committed
2174
    do {
2175
	MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
2176

Alexandre Julliard's avatar
Alexandre Julliard committed
2177 2178 2179
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
	mii.fType = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2180
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
2181
	mii.fState = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2182
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
2183
	mii.wID = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2184 2185 2186 2187
        res += sizeof(DWORD);
	resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
        res += sizeof(WORD);
	/* Align the text on a word boundary.  */
2188
	res += (~((UINT_PTR)res - 1)) & 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2189
	mii.dwTypeData = (LPWSTR) res;
2190
	res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
2191
	/* Align the following fields on a dword boundary.  */
2192
	res += (~((UINT_PTR)res - 1)) & 3;
Alexandre Julliard's avatar
Alexandre Julliard committed
2193

2194 2195
        TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
              mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
Alexandre Julliard's avatar
Alexandre Julliard committed
2196

Alexandre Julliard's avatar
Alexandre Julliard committed
2197
	if (resinfo & 1) {	/* Pop-up? */
2198
	    /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
2199
	    res += sizeof(DWORD);
2200
	    mii.hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
2201 2202 2203
	    if (!mii.hSubMenu)
		return NULL;
	    if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2204
		DestroyMenu(mii.hSubMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
2205
                return NULL;
2206
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2207 2208
	    mii.fMask |= MIIM_SUBMENU;
	    mii.fType |= MF_POPUP;
Alexandre Julliard's avatar
Alexandre Julliard committed
2209
        }
2210 2211 2212 2213 2214 2215
	else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
	{
	    WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
		mii.wID, mii.fType);
	    mii.fType |= MF_SEPARATOR;
	}
2216
	InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
Alexandre Julliard's avatar
Alexandre Julliard committed
2217 2218 2219 2220 2221
    } while (!(resinfo & MF_END));
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2222 2223 2224 2225 2226
/***********************************************************************
 *           MENU_GetSubPopup
 *
 * Return the handle of the selected sub-popup menu (if any).
 */
2227
static HMENU MENU_GetSubPopup( HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2228
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2229 2230 2231
    POPUPMENU *menu;
    MENUITEM *item;

2232
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2233

2234
    if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2235

Alexandre Julliard's avatar
Alexandre Julliard committed
2236
    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2237 2238
    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
        return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2239
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2240 2241 2242
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2243 2244 2245 2246 2247
/***********************************************************************
 *           MENU_HideSubPopups
 *
 * Hide the sub-popup menus of this menu.
 */
2248
static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2249
                                BOOL sendMenuSelect, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2250
{
2251
    POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2252

2253
    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2254

2255
    if (menu && top_popup)
Alexandre Julliard's avatar
Alexandre Julliard committed
2256
    {
2257
	HMENU hsubmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2258 2259 2260 2261 2262 2263
	POPUPMENU *submenu;
	MENUITEM *item;

	if (menu->FocusedItem != NO_SELECTED_ITEM)
	{
	    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2264 2265 2266 2267
	    if (!(item->fType & MF_POPUP) ||
		!(item->fState & MF_MOUSESELECT)) return;
	    item->fState &= ~MF_MOUSESELECT;
	    hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2268 2269
	} else return;

2270
	if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2271
	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2272
	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2273 2274
        DestroyWindow( submenu->hWnd );
        submenu->hWnd = 0;
2275 2276 2277 2278

        if (!(wFlags & TPM_NONOTIFY))
           SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
                         MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2279
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2280 2281 2282
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2283 2284 2285 2286 2287 2288
/***********************************************************************
 *           MENU_ShowSubPopup
 *
 * Display the sub-menu of the selected item of this menu.
 * Return the handle of the submenu, or hmenu if no submenu to display.
 */
2289
static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2290
                                  BOOL selectFirst, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2291
{
2292
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
2293 2294
    POPUPMENU *menu;
    MENUITEM *item;
2295
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
2296

2297
    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2298

2299
    if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2300

2301
    if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2302

Alexandre Julliard's avatar
Alexandre Julliard committed
2303
    item = &menu->items[menu->FocusedItem];
2304
    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2305
        return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2306

2307 2308
    /* message must be sent before using item,
       because nearly everything may be changed by the application ! */
Alexandre Julliard's avatar
Alexandre Julliard committed
2309

2310 2311
    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
2312
       SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2313
                     MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
Alexandre Julliard's avatar
Alexandre Julliard committed
2314

Alexandre Julliard's avatar
Alexandre Julliard committed
2315 2316 2317
    item = &menu->items[menu->FocusedItem];
    rect = item->rect;

2318
    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2319
    if (!(item->fState & MF_HILITE))
Alexandre Julliard's avatar
Alexandre Julliard committed
2320
    {
2321 2322
        if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
        else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2323

2324
        SelectObject( hdc, get_menu_font(FALSE));
2325

Alexandre Julliard's avatar
Alexandre Julliard committed
2326
        item->fState |= MF_HILITE;
2327
        MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2328
	ReleaseDC( menu->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
2329 2330 2331 2332
    }
    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
      item->rect = rect;

Alexandre Julliard's avatar
Alexandre Julliard committed
2333
    item->fState |= MF_MOUSESELECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
2334 2335

    if (IS_SYSTEM_MENU(menu))
Alexandre Julliard's avatar
Alexandre Julliard committed
2336
    {
2337
	MENU_InitSysMenuPopup(item->hSubMenu,
2338 2339
                              GetWindowLongW( menu->hWnd, GWL_STYLE ),
                              GetClassLongW( menu->hWnd, GCL_STYLE));
Alexandre Julliard's avatar
Alexandre Julliard committed
2340

2341
	NC_GetSysPopupPos( menu->hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
2342
	rect.top = rect.bottom;
2343 2344
	rect.right = GetSystemMetrics(SM_CXSIZE);
        rect.bottom = GetSystemMetrics(SM_CYSIZE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2345 2346 2347
    }
    else
    {
2348
        GetWindowRect( menu->hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
2349 2350
	if (menu->wFlags & MF_POPUP)
	{
2351 2352 2353
            RECT rc = item->rect;

            MENU_AdjustMenuItemRect(menu, &rc);
2354 2355 2356

	    /* The first item in the popup menu has to be at the
	       same y position as the focused menu item */
2357
	    rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2358
	    rect.top += rc.top - MENU_TOP_MARGIN;
2359
	    rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2360 2361
	    rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
	                  - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
Alexandre Julliard's avatar
Alexandre Julliard committed
2362 2363 2364
	}
	else
	{
2365 2366
	    rect.left += item->rect.left;
	    rect.top += item->rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
2367 2368 2369
	    rect.right = item->rect.right - item->rect.left;
	    rect.bottom = item->rect.bottom - item->rect.top;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2370
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2371

2372
    MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
Alexandre Julliard's avatar
Alexandre Julliard committed
2373
		    rect.left, rect.top, rect.right, rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
2374
    if (selectFirst)
Alexandre Julliard's avatar
Alexandre Julliard committed
2375 2376
        MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
    return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2377
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2378

2379 2380 2381 2382 2383


/**********************************************************************
 *         MENU_IsMenuActive
 */
2384
HWND MENU_IsMenuActive(void)
2385
{
2386
    return top_popup;
2387 2388
}

2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402
/**********************************************************************
 *         MENU_EndMenu
 *
 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
 *
 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
 */
void MENU_EndMenu( HWND hwnd )
{
    POPUPMENU *menu;
    menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
    if (menu && hwnd == menu->hwndOwner) EndMenu();
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2403
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2404
 *           MENU_PtMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2405
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2406 2407
 * Walks menu chain trying to find a menu pt maps to.
 */
2408
static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
2409
{
2410
   POPUPMENU *menu = MENU_GetMenu( hMenu );
2411 2412
   UINT item = menu->FocusedItem;
   HMENU ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2413 2414

   /* try subpopup first (if any) */
2415 2416 2417 2418
   ret = (item != NO_SELECTED_ITEM &&
          (menu->items[item].fType & MF_POPUP) &&
          (menu->items[item].fState & MF_MOUSESELECT))
        ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2419

2420
   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
Alexandre Julliard's avatar
Alexandre Julliard committed
2421
   {
2422 2423 2424 2425 2426 2427 2428 2429 2430
       INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
       if( menu->wFlags & MF_POPUP )
       {
           if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
       }
       else if (ht == HTSYSMENU)
           ret = get_win_sys_menu( menu->hWnd );
       else if (ht == HTMENU)
           ret = GetMenu( menu->hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2431
   }
2432
   return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2433
}
2434

Alexandre Julliard's avatar
Alexandre Julliard committed
2435 2436 2437 2438
/***********************************************************************
 *           MENU_ExecFocusedItem
 *
 * Execute a menu item (for instance when user pressed Enter).
2439
 * Return the wID of the executed item. Otherwise, -1 indicating
2440
 * that no menu item was executed, -2 if a popup is shown;
2441 2442 2443
 * Have to receive the flags for the TrackPopupMenu options to avoid
 * sending unwanted message.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2444
 */
2445
static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2446 2447
{
    MENUITEM *item;
2448
    POPUPMENU *menu = MENU_GetMenu( hMenu );
2449

2450
    TRACE("%p hmenu=%p\n", pmt, hMenu);
2451

2452
    if (!menu || !menu->nItems ||
2453
	(menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2454

Alexandre Julliard's avatar
Alexandre Julliard committed
2455
    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2456

2457
    TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
Alexandre Julliard's avatar
Alexandre Julliard committed
2458 2459

    if (!(item->fType & MF_POPUP))
Alexandre Julliard's avatar
Alexandre Julliard committed
2460
    {
2461
	if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2462
	{
2463 2464
	    /* If TPM_RETURNCMD is set you return the id, but
	       do not send a message to the owner */
2465
	    if(!(wFlags & TPM_RETURNCMD))
Alexandre Julliard's avatar
Alexandre Julliard committed
2466
	    {
2467
		if( menu->wFlags & MF_SYSMENU )
2468
		    PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2469 2470
				  MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
		else
2471
		{
2472 2473 2474 2475
                    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
                    DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);

                    if (dwStyle & MNS_NOTIFYBYPOS)
2476 2477 2478 2479 2480
                        PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
                                      (LPARAM)hMenu);
                    else
                        PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
2481
	    }
2482
	    return item->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
2483
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2484 2485
    }
    else
2486
    {
2487
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2488 2489
	return -2;
    }
2490

2491
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2492 2493
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2494
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2495
 *           MENU_SwitchTracking
Alexandre Julliard's avatar
Alexandre Julliard committed
2496
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2497
 * Helper function for menu navigation routines.
Alexandre Julliard's avatar
Alexandre Julliard committed
2498
 */
2499
static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2500
{
2501 2502
    POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2503

2504
    TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2505

Alexandre Julliard's avatar
Alexandre Julliard committed
2506 2507
    if( pmt->hTopMenu != hPtMenu &&
	!((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2508
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2509
	/* both are top level menus (system and menu-bar) */
2510
	MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2511
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2512 2513
        pmt->hTopMenu = hPtMenu;
    }
2514
    else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2515
    MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2516 2517 2518 2519 2520 2521 2522 2523
}


/***********************************************************************
 *           MENU_ButtonDown
 *
 * Return TRUE if we can go on with menu tracking.
 */
2524
static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2525
{
2526
    TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2527

Alexandre Julliard's avatar
Alexandre Julliard committed
2528
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2529
    {
2530
	UINT id = 0;
2531
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2532 2533 2534 2535 2536 2537 2538 2539
	MENUITEM *item;

	if( IS_SYSTEM_MENU(ptmenu) )
	    item = ptmenu->items;
	else
	    item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );

	if( item )
Alexandre Julliard's avatar
Alexandre Julliard committed
2540
	{
2541
	    if( ptmenu->FocusedItem != id )
2542
		MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2543

2544 2545
	    /* If the popup menu is not already "popped" */
	    if(!(item->fState & MF_MOUSESELECT ))
2546
	    {
2547
		pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2548 2549
	    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2550
	    return TRUE;
2551
	}
2552
	/* Else the click was on the menu bar, finish the tracking */
Alexandre Julliard's avatar
Alexandre Julliard committed
2553
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2554
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2555 2556
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2557 2558 2559
/***********************************************************************
 *           MENU_ButtonUp
 *
2560 2561
 * Return the value of MENU_ExecFocusedItem if
 * the selected item was not a popup. Else open the popup.
2562
 * A -1 return value indicates that we go on with menu tracking.
2563
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2564
 */
2565
static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
2566
{
2567
    TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2568

Alexandre Julliard's avatar
Alexandre Julliard committed
2569
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2570
    {
2571
	UINT id = 0;
2572
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2573
	MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2574

Alexandre Julliard's avatar
Alexandre Julliard committed
2575 2576 2577 2578
        if( IS_SYSTEM_MENU(ptmenu) )
            item = ptmenu->items;
        else
            item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
Alexandre Julliard's avatar
Alexandre Julliard committed
2579

Alexandre Julliard's avatar
Alexandre Julliard committed
2580
	if( item && (ptmenu->FocusedItem == id ))
Alexandre Julliard's avatar
Alexandre Julliard committed
2581
	{
2582 2583
            debug_print_menuitem ("FocusedItem: ", item, "");

Alexandre Julliard's avatar
Alexandre Julliard committed
2584
	    if( !(item->fType & MF_POPUP) )
2585 2586
	    {
	        INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2587 2588
                if (executedMenuId == -1 || executedMenuId == -2) return -1;
                return executedMenuId;
2589
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2590

2591 2592 2593
	    /* If we are dealing with the top-level menu            */
	    /* and this is a click on an already "popped" item:     */
	    /* Stop the menu tracking and close the opened submenus */
2594
	    if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2595
		return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2596
	}
2597
	ptmenu->bTimeToHide = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2598
    }
2599
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2600 2601 2602
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2603 2604 2605 2606 2607
/***********************************************************************
 *           MENU_MouseMove
 *
 * Return TRUE if we can go on with menu tracking.
 */
2608
static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2609
{
2610
    UINT id = NO_SELECTED_ITEM;
Alexandre Julliard's avatar
Alexandre Julliard committed
2611
    POPUPMENU *ptmenu = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
2612

Alexandre Julliard's avatar
Alexandre Julliard committed
2613
    if( hPtMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2614
    {
2615
	ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2616 2617 2618 2619
        if( IS_SYSTEM_MENU(ptmenu) )
	    id = 0;
        else
            MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2620
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2621 2622

    if( id == NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
2623
    {
2624
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2625
			 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2626

Alexandre Julliard's avatar
Alexandre Julliard committed
2627
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2628
    else if( ptmenu->FocusedItem != id )
Alexandre Julliard's avatar
Alexandre Julliard committed
2629
    {
2630
	    MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2631
	    pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2632 2633
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2634 2635
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2636

Alexandre Julliard's avatar
Alexandre Julliard committed
2637 2638
/***********************************************************************
 *           MENU_DoNextMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2639 2640
 *
 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
Alexandre Julliard's avatar
Alexandre Julliard committed
2641
 */
2642
static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2643
{
2644
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2645
    BOOL atEnd = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2646

2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657
    /* When skipping left, we need to do something special after the
       first menu.                                                  */
    if (vk == VK_LEFT && menu->FocusedItem == 0)
    {
        atEnd = TRUE;
    }
    /* When skipping right, for the non-system menu, we need to
       handle the last non-special menu item (ie skip any window
       icons such as MDI maximize, restore or close)             */
    else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
    {
2658
        UINT i = menu->FocusedItem + 1;
2659
        while (i < menu->nItems) {
2660 2661 2662 2663 2664
            if ((menu->items[i].wID >= SC_SIZE &&
                 menu->items[i].wID <= SC_RESTORE)) {
                i++;
            } else break;
        }
2665
        if (i == menu->nItems) {
2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677
            atEnd = TRUE;
        }
    }
    /* When skipping right, we need to cater for the system menu */
    else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
    {
        if (menu->FocusedItem == (menu->nItems - 1)) {
            atEnd = TRUE;
        }
    }

    if( atEnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
2678
    {
2679
        MDINEXTMENU next_menu;
2680 2681 2682
	HMENU hNewMenu;
	HWND  hNewWnd;
	UINT  id = 0;
2683 2684 2685 2686 2687

        next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
        next_menu.hmenuNext = 0;
        next_menu.hwndNext = 0;
        SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2688

2689
        TRACE("%p [%p] -> %p [%p]\n",
2690
              pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
2691

2692
	if (!next_menu.hmenuNext || !next_menu.hwndNext)
Alexandre Julliard's avatar
Alexandre Julliard committed
2693
	{
2694
            DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2695 2696 2697 2698
	    hNewWnd = pmt->hOwnerWnd;
	    if( IS_SYSTEM_MENU(menu) )
	    {
		/* switch to the menu bar */
Alexandre Julliard's avatar
Alexandre Julliard committed
2699

2700
                if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2701

Alexandre Julliard's avatar
Alexandre Julliard committed
2702 2703
	        if( vk == VK_LEFT )
	        {
2704
		    menu = MENU_GetMenu( hNewMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2705
		    id = menu->nItems - 1;
2706 2707 2708 2709 2710 2711

                    /* Skip backwards over any system predefined icons,
                       eg. MDI close, restore etc icons                 */
                    while ((id > 0) &&
                           (menu->items[id].wID >= SC_SIZE &&
                            menu->items[id].wID <= SC_RESTORE)) id--;
Alexandre Julliard's avatar
Alexandre Julliard committed
2712 2713
	        }
	    }
2714
	    else if (style & WS_SYSMENU )
Alexandre Julliard's avatar
Alexandre Julliard committed
2715 2716
	    {
		/* switch to the system menu */
2717
	        hNewMenu = get_win_sys_menu( hNewWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2718
	    }
2719
            else return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2720 2721 2722
	}
	else    /* application returned a new menu to switch to */
	{
2723
            hNewMenu = next_menu.hmenuNext;
2724
            hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
2725

2726
	    if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2727
	    {
2728
                DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2729

2730 2731
		if (style & WS_SYSMENU &&
		    GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2732 2733
		{
	            /* get the real system menu */
2734
		    hNewMenu =  get_win_sys_menu(hNewWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2735
		}
2736
	        else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2737
		{
2738 2739
		    /* FIXME: Not sure what to do here;
		     * perhaps try to track hNewMenu as a popup? */
Alexandre Julliard's avatar
Alexandre Julliard committed
2740

2741
		    TRACE(" -- got confused.\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2742 2743 2744 2745 2746 2747 2748
		    return FALSE;
		}
	    }
	    else return FALSE;
	}

	if( hNewMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2749
	{
2750
	    MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2751
                    FALSE, 0 );
2752
	    if( pmt->hCurrentMenu != pmt->hTopMenu )
2753
		MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2754 2755
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
2756 2757 2758
	if( hNewWnd != pmt->hOwnerWnd )
	{
	    pmt->hOwnerWnd = hNewWnd;
2759
            set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2760
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2761

Alexandre Julliard's avatar
Alexandre Julliard committed
2762
	pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2763
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2764

Alexandre Julliard's avatar
Alexandre Julliard committed
2765 2766 2767 2768
	return TRUE;
    }
    return FALSE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2769

Alexandre Julliard's avatar
Alexandre Julliard committed
2770 2771 2772 2773 2774 2775
/***********************************************************************
 *           MENU_SuspendPopup
 *
 * The idea is not to show the popup if the next input message is
 * going to hide it anyway.
 */
2776
static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
Alexandre Julliard's avatar
Alexandre Julliard committed
2777
{
2778
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
2779

Alexandre Julliard's avatar
Alexandre Julliard committed
2780
    msg.hwnd = pmt->hOwnerWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
2781

2782
    PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2783 2784 2785 2786 2787
    pmt->trackFlags |= TF_SKIPREMOVE;

    switch( uMsg )
    {
	case WM_KEYDOWN:
2788
	     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2789 2790
	     if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
	     {
2791 2792
		 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
	         PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2793 2794 2795 2796 2797 2798 2799 2800
	         if( msg.message == WM_KEYDOWN &&
		    (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
	         {
		     pmt->trackFlags |= TF_SUSPENDPOPUP;
		     return TRUE;
	         }
	     }
	     break;
Alexandre Julliard's avatar
Alexandre Julliard committed
2801
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2802

Alexandre Julliard's avatar
Alexandre Julliard committed
2803 2804 2805
    /* failures go through this */
    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2806
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2807

2808 2809 2810
/***********************************************************************
 *           MENU_KeyEscape
 *
2811
 * Handle a VK_ESCAPE key event in a menu.
2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833
 */
static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
{
    BOOL bEndMenu = TRUE;

    if (pmt->hCurrentMenu != pmt->hTopMenu)
    {
        POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);

        if (menu->wFlags & MF_POPUP)
        {
            HMENU hmenutmp, hmenuprev;

            hmenuprev = hmenutmp = pmt->hTopMenu;

            /* close topmost popup */
            while (hmenutmp != pmt->hCurrentMenu)
            {
                hmenuprev = hmenutmp;
                hmenutmp = MENU_GetSubPopup( hmenuprev );
            }

2834
            MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2835 2836 2837 2838 2839 2840 2841 2842
            pmt->hCurrentMenu = hmenuprev;
            bEndMenu = FALSE;
        }
    }

    return bEndMenu;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2843 2844 2845
/***********************************************************************
 *           MENU_KeyLeft
 *
2846
 * Handle a VK_LEFT key event in a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2847
 */
2848
static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2849
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2850
    POPUPMENU *menu;
2851 2852
    HMENU hmenutmp, hmenuprev;
    UINT  prevcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2853

Alexandre Julliard's avatar
Alexandre Julliard committed
2854
    hmenuprev = hmenutmp = pmt->hTopMenu;
2855
    menu = MENU_GetMenu( hmenutmp );
Alexandre Julliard's avatar
Alexandre Julliard committed
2856

Alexandre Julliard's avatar
Alexandre Julliard committed
2857
    /* Try to move 1 column left (if possible) */
2858
    if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2859
	NO_SELECTED_ITEM ) {
2860

Alexandre Julliard's avatar
Alexandre Julliard committed
2861
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2862
			 prevcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2863 2864 2865
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2866 2867
    /* close topmost popup */
    while (hmenutmp != pmt->hCurrentMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2868
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2869
	hmenuprev = hmenutmp;
Alexandre Julliard's avatar
Alexandre Julliard committed
2870 2871
	hmenutmp = MENU_GetSubPopup( hmenuprev );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2872

2873
    MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2874
    pmt->hCurrentMenu = hmenuprev;
Alexandre Julliard's avatar
Alexandre Julliard committed
2875 2876

    if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2877
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2878
	/* move menu bar selection if no more popups are left */
Alexandre Julliard's avatar
Alexandre Julliard committed
2879

2880
	if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2881
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
2882

Alexandre Julliard's avatar
Alexandre Julliard committed
2883
	if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
Alexandre Julliard's avatar
Alexandre Julliard committed
2884
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2885 2886 2887 2888
	   /* A sublevel menu was displayed - display the next one
	    * unless there is another displacement coming up */

	    if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2889 2890
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
						pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2891
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2892
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2893 2894 2895
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2896 2897 2898 2899 2900
/***********************************************************************
 *           MENU_KeyRight
 *
 * Handle a VK_RIGHT key event in a menu.
 */
2901
static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2902
{
2903
    HMENU hmenutmp;
2904
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2905
    UINT  nextcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2906

2907 2908 2909 2910
    TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
          pmt->hCurrentMenu,
          debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
          pmt->hTopMenu, debugstr_w(menu->items[0].text) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2911

Alexandre Julliard's avatar
Alexandre Julliard committed
2912
    if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
Alexandre Julliard's avatar
Alexandre Julliard committed
2913
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2914
	/* If already displaying a popup, try to display sub-popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
2915

Alexandre Julliard's avatar
Alexandre Julliard committed
2916
	hmenutmp = pmt->hCurrentMenu;
2917
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2918

Alexandre Julliard's avatar
Alexandre Julliard committed
2919 2920
	/* if subpopup was displayed then we are done */
	if (hmenutmp != pmt->hCurrentMenu) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2921
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2922

Alexandre Julliard's avatar
Alexandre Julliard committed
2923
    /* Check to see if there's another column */
2924
    if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2925
	NO_SELECTED_ITEM ) {
2926
	TRACE("Going to %d.\n", nextcol );
Alexandre Julliard's avatar
Alexandre Julliard committed
2927
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2928
			 nextcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2929 2930 2931
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2932
    if (!(menu->wFlags & MF_POPUP))	/* menu bar tracking */
Alexandre Julliard's avatar
Alexandre Julliard committed
2933
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2934
	if( pmt->hCurrentMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2935
	{
2936
	    MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2937 2938 2939 2940
	    hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
	} else hmenutmp = 0;

	/* try to move to the next item */
2941
	if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2942 2943 2944 2945
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );

	if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
	    if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2946
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2947
						       pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2948 2949
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2950

2951 2952 2953 2954 2955
static void CALLBACK release_capture( BOOL __normal )
{
    set_capture_window( 0, GUI_INMENUMODE, NULL );
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2956 2957 2958 2959 2960
/***********************************************************************
 *           MENU_TrackMenu
 *
 * Menu tracking code.
 */
2961 2962
static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
                            HWND hwnd, const RECT *lprect )
Alexandre Julliard's avatar
Alexandre Julliard committed
2963
{
2964
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
2965
    POPUPMENU *menu;
2966
    BOOL fRemove;
2967
    INT executedMenuId = -1;
2968
    MTRACKER mt;
2969
    BOOL enterIdleSent = FALSE;
2970
    HWND capture_win;
2971 2972 2973 2974

    mt.trackFlags = 0;
    mt.hCurrentMenu = hmenu;
    mt.hTopMenu = hmenu;
2975
    mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2976 2977
    mt.pt.x = x;
    mt.pt.y = y;
Alexandre Julliard's avatar
Alexandre Julliard committed
2978

2979 2980
    TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
          hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2981

Alexandre Julliard's avatar
Alexandre Julliard committed
2982
    fEndMenu = FALSE;
2983 2984
    if (!(menu = MENU_GetMenu( hmenu )))
    {
2985
        WARN("Invalid menu handle %p\n", hmenu);
2986 2987 2988
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2989

2990
    if (wFlags & TPM_BUTTONDOWN)
2991 2992 2993
    {
	/* Get the result in order to start the tracking or not */
	fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2994
	fEndMenu = !fRemove;
2995
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2996

2997 2998
    if (wFlags & TF_ENDMENU) fEndMenu = TRUE;

2999 3000 3001
    /* owner may not be visible when tracking a popup, so use the menu itself */
    capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
    set_capture_window( capture_win, GUI_INMENUMODE, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
3002

3003
    __TRY while (!fEndMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
3004
    {
3005
	menu = MENU_GetMenu( mt.hCurrentMenu );
3006 3007
	if (!menu) /* sometimes happens if I do a window manager close */
	    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3008

Alexandre Julliard's avatar
Alexandre Julliard committed
3009
	/* we have to keep the message in the queue until it's
Alexandre Julliard's avatar
Alexandre Julliard committed
3010
	 * clear that menu loop is not over yet. */
Alexandre Julliard's avatar
Alexandre Julliard committed
3011

3012 3013
        for (;;)
        {
3014
            if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3015
            {
3016
                if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3017
                /* remove the message from the queue */
3018
                PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3019 3020 3021 3022 3023
            }
            else
            {
                if (!enterIdleSent)
                {
3024
                    HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3025 3026 3027 3028 3029 3030
                    enterIdleSent = TRUE;
                    SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
                }
                WaitMessage();
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3031

3032
	/* check if EndMenu() tried to cancel us, by posting this message */
3033
        if(msg.message == WM_CANCELMODE)
3034 3035 3036 3037 3038
	{
	    /* we are now out of the loop */
    	    fEndMenu = TRUE;

	    /* remove the message from the queue */
3039
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3040

3041 3042 3043 3044
	    /* break out of internal loop, ala ESCAPE */
	    break;
	}

3045
        TranslateMessage( &msg );
3046
        mt.pt = msg.pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
3047

3048 3049 3050
	if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
	  enterIdleSent=FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
3051
        fRemove = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3052
	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
3053
	{
3054
            /*
3055 3056 3057
             * Use the mouse coordinates in lParam instead of those in the MSG
             * struct to properly handle synthetic messages. They are already
             * in screen coordinates.
3058
             */
3059 3060
            mt.pt.x = (short)LOWORD(msg.lParam);
            mt.pt.y = (short)HIWORD(msg.lParam);
3061

Alexandre Julliard's avatar
Alexandre Julliard committed
3062
	    /* Find a menu for this mouse event */
3063
	    hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
Alexandre Julliard's avatar
Alexandre Julliard committed
3064

Alexandre Julliard's avatar
Alexandre Julliard committed
3065
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
3066
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3067 3068
		/* no WM_NC... messages in captured state */

Alexandre Julliard's avatar
Alexandre Julliard committed
3069 3070 3071 3072 3073 3074
		case WM_RBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONDBLCLK:
		case WM_LBUTTONDOWN:
3075 3076 3077 3078
		    /* If the message belongs to the menu, removes it from the queue */
		    /* Else, end menu tracking */
		    fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
		    fEndMenu = !fRemove;
Alexandre Julliard's avatar
Alexandre Julliard committed
3079
		    break;
3080

Alexandre Julliard's avatar
Alexandre Julliard committed
3081 3082 3083 3084
		case WM_RBUTTONUP:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONUP:
3085 3086
		    /* Check if a menu was selected by the mouse */
		    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
3087
		    {
3088
                        executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3089
                        TRACE("executedMenuId %d\n", executedMenuId);
3090 3091

			/* End the loop if executedMenuId is an item ID */
3092 3093
			/* or if the job was done (executedMenuId = 0). */
                        fEndMenu = fRemove = (executedMenuId != -1);
Alexandre Julliard's avatar
Alexandre Julliard committed
3094
		    }
3095 3096 3097 3098 3099
                    /* No menu was selected by the mouse */
                    /* if the function was called by TrackPopupMenu, continue
                       with the menu tracking. If not, stop it */
                    else
                        fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3100

Alexandre Julliard's avatar
Alexandre Julliard committed
3101
		    break;
3102

Alexandre Julliard's avatar
Alexandre Julliard committed
3103
		case WM_MOUSEMOVE:
3104 3105
                    /* the selected menu item must be changed every time */
		     /* the mouse moves. */
3106

3107
                    if (hmenu)
3108
			fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3109

Alexandre Julliard's avatar
Alexandre Julliard committed
3110
	    } /* switch(msg.message) - mouse */
Alexandre Julliard's avatar
Alexandre Julliard committed
3111
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
3112
	else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
3113
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
3114
            fRemove = TRUE;  /* Keyboard messages are always removed */
Alexandre Julliard's avatar
Alexandre Julliard committed
3115
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
3116 3117
	    {
	    case WM_KEYDOWN:
3118
	    case WM_SYSKEYDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
3119
		switch(msg.wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3120
		{
3121
		case VK_MENU:
3122
		case VK_F10:
3123 3124 3125
		    fEndMenu = TRUE;
		    break;

Alexandre Julliard's avatar
Alexandre Julliard committed
3126 3127
		case VK_HOME:
		case VK_END:
3128
		    MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3129
				     NO_SELECTED_ITEM, FALSE, 0 );
3130
		    MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3131
				       (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
3132 3133
		    break;

3134
		case VK_UP:
Alexandre Julliard's avatar
Alexandre Julliard committed
3135 3136
		case VK_DOWN: /* If on menu bar, pull-down the menu */

3137
		    menu = MENU_GetMenu( mt.hCurrentMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3138
		    if (!(menu->wFlags & MF_POPUP))
3139
			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3140
		    else      /* otherwise try to move selection */
3141 3142
			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
                                       (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
Alexandre Julliard's avatar
Alexandre Julliard committed
3143 3144 3145
		    break;

		case VK_LEFT:
3146
		    MENU_KeyLeft( &mt, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3147
		    break;
3148

Alexandre Julliard's avatar
Alexandre Julliard committed
3149
		case VK_RIGHT:
3150
		    MENU_KeyRight( &mt, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3151
		    break;
3152

Alexandre Julliard's avatar
Alexandre Julliard committed
3153
		case VK_ESCAPE:
3154
                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3155 3156
		    break;

3157 3158 3159 3160 3161
		case VK_F1:
		    {
			HELPINFO hi;
			hi.cbSize = sizeof(HELPINFO);
			hi.iContextType = HELPINFO_MENUITEM;
3162
			if (menu->FocusedItem == NO_SELECTED_ITEM)
3163
			    hi.iCtrlId = 0;
3164 3165
		        else
			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3166 3167 3168
			hi.hItemHandle = hmenu;
			hi.dwContextId = menu->dwContextHelpID;
			hi.MousePos = msg.pt;
3169
			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3170 3171 3172
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3173 3174 3175 3176 3177 3178
		default:
		    break;
		}
		break;  /* WM_KEYDOWN */

	    case WM_CHAR:
3179
	    case WM_SYSCHAR:
Alexandre Julliard's avatar
Alexandre Julliard committed
3180
		{
3181
		    UINT	pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
3182

3183 3184
		    if (msg.wParam == '\r' || msg.wParam == ' ')
		    {
3185
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3186
                        fEndMenu = (executedMenuId != -2);
3187

3188 3189 3190
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3191 3192
		      /* Hack to avoid control chars. */
		      /* We will find a better way real soon... */
3193
		    if (msg.wParam < 32) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3194

3195
		    pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3196
                                              LOWORD(msg.wParam), FALSE );
3197 3198
		    if (pos == (UINT)-2) fEndMenu = TRUE;
		    else if (pos == (UINT)-1) MessageBeep(0);
Alexandre Julliard's avatar
Alexandre Julliard committed
3199 3200
		    else
		    {
3201 3202
			MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
                                TRUE, 0 );
3203
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3204
                        fEndMenu = (executedMenuId != -2);
Alexandre Julliard's avatar
Alexandre Julliard committed
3205
		    }
3206
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
3207 3208
		break;
	    }  /* switch(msg.message) - kbd */
Alexandre Julliard's avatar
Alexandre Julliard committed
3209 3210 3211
	}
	else
	{
3212
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3213
	    DispatchMessageW( &msg );
3214
	    continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
3215 3216
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
3217 3218 3219 3220 3221
	if (!fEndMenu) fRemove = TRUE;

	/* finally remove message from the queue */

        if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3222
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
Alexandre Julliard's avatar
Alexandre Julliard committed
3223
	else mt.trackFlags &= ~TF_SKIPREMOVE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3224
    }
3225
    __FINALLY( release_capture )
3226

3227 3228 3229 3230 3231
    /* If dropdown is still painted and the close box is clicked on
       then the menu will be destroyed as part of the DispatchMessage above.
       This will then invalidate the menu handle in mt.hTopMenu. We should
       check for this first.  */
    if( IsMenu( mt.hTopMenu ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
3232
    {
3233
	menu = MENU_GetMenu( mt.hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3234

3235 3236
        if( IsWindow( mt.hOwnerWnd ) )
        {
3237
	    MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3238

3239
	    if (menu && (menu->wFlags & MF_POPUP))
3240
	    {
3241 3242
                DestroyWindow( menu->hWnd );
                menu->hWnd = 0;
3243 3244 3245 3246

                if (!(wFlags & TPM_NONOTIFY))
                   SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
                                 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3247 3248
	    }
	    MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3249
	    SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3250 3251 3252 3253
        }

        /* Reset the variable for hiding menu */
        if( menu ) menu->bTimeToHide = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3254
    }
3255

3256
    /* The return value is only used by TrackPopupMenu */
3257
    if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3258
    if (executedMenuId == -1) executedMenuId = 0;
3259
    return executedMenuId;
Alexandre Julliard's avatar
Alexandre Julliard committed
3260
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3261

Alexandre Julliard's avatar
Alexandre Julliard committed
3262 3263 3264
/***********************************************************************
 *           MENU_InitTracking
 */
3265
static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
3266
{
3267 3268
    POPUPMENU *menu;
    
3269
    TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3270

3271
    HideCaret(0);
3272

3273 3274 3275 3276 3277 3278
    /* This makes the menus of applications built with Delphi work.
     * It also enables menus to be displayed in more than one window,
     * but there are some bugs left that need to be fixed in this case.
     */
    if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;

3279 3280
    /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
3281
       SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3282

3283
    SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3284 3285

    if (!(wFlags & TPM_NONOTIFY))
3286
    {
3287
       SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3288 3289 3290
       /* If an app changed/recreated menu bar entries in WM_INITMENU
        * menu sizes will be recalculated once the menu created/shown.
        */
3291
    }
3292

3293 3294
    return TRUE;
}
3295

3296 3297 3298
/***********************************************************************
 *           MENU_ExitTracking
 */
3299
static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3300
{
3301
    TRACE("hwnd=%p\n", hWnd);
3302

3303
    SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3304
    ShowCaret(0);
3305
    top_popup = 0;
3306
    top_popup_hmenu = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3307 3308 3309
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3310 3311 3312 3313 3314
/***********************************************************************
 *           MENU_TrackMouseMenuBar
 *
 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
 */
3315
void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
3316
{
3317
    HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3318
    UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
3319

3320
    TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3321

3322
    if (IsMenu(hMenu))
Alexandre Julliard's avatar
Alexandre Julliard committed
3323
    {
3324 3325
	MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
	MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3326
	MENU_ExitTracking(hWnd, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3327
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3328 3329 3330 3331 3332 3333 3334 3335
}


/***********************************************************************
 *           MENU_TrackKbdMenuBar
 *
 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
 */
3336
void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
Alexandre Julliard's avatar
Alexandre Julliard committed
3337
{
3338 3339
    UINT uItem = NO_SELECTED_ITEM;
    HMENU hTrackMenu;
3340
    UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
3341

3342 3343
    TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);

Alexandre Julliard's avatar
Alexandre Julliard committed
3344
    /* find window that has a menu */
3345

3346 3347
    while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
        if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
3348

Alexandre Julliard's avatar
Alexandre Julliard committed
3349
    /* check if we have to track a system menu */
3350 3351

    hTrackMenu = GetMenu( hwnd );
3352
    if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
Alexandre Julliard's avatar
Alexandre Julliard committed
3353
    {
3354
        if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3355 3356 3357
        hTrackMenu = get_win_sys_menu( hwnd );
        uItem = 0;
        wParam |= HTSYSMENU; /* prevent item lookup */
Alexandre Julliard's avatar
Alexandre Julliard committed
3358
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3359

3360 3361 3362
    if (!IsMenu( hTrackMenu )) return;

    MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3363

3364
    if( wChar && wChar != ' ' )
3365
    {
3366
        uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3367
        if ( uItem >= (UINT)(-2) )
Alexandre Julliard's avatar
Alexandre Julliard committed
3368
        {
3369
            if( uItem == (UINT)(-1) ) MessageBeep(0);
3370
            /* schedule end of menu tracking */
3371
            wFlags |= TF_ENDMENU;
3372
            goto track_menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3373
        }
3374
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3375

3376
    MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3377

3378
    if (!(wParam & HTSYSMENU) || wChar == ' ')
3379 3380 3381 3382 3383 3384
    {
        if( uItem == NO_SELECTED_ITEM )
            MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
        else
            PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
    }
3385 3386 3387

track_menu:
    MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3388
    MENU_ExitTracking( hwnd, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
3389 3390
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3391
/**********************************************************************
3392
 *           TrackPopupMenuEx   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3393
 */
3394 3395
BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
                              HWND hWnd, LPTPMPARAMS lpTpm )
Alexandre Julliard's avatar
Alexandre Julliard committed
3396
{
3397
    POPUPMENU *menu;
3398
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3399

3400 3401 3402 3403 3404 3405 3406
    TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
            hMenu, wFlags, x, y, hWnd, lpTpm,
            lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );

    /* Parameter check */
    /* FIXME: this check is performed several times, here and in the called
       functions. That could be optimized */
3407
    if (!(menu = MENU_GetMenu( hMenu )))
3408 3409 3410 3411
    {
        SetLastError( ERROR_INVALID_MENU_HANDLE );
        return FALSE;
    }
3412

3413 3414 3415 3416 3417 3418
    if (IsWindow(menu->hWnd))
    {
        SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
        return FALSE;
    }

3419 3420 3421 3422
    MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);

    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
3423
        SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3424

3425
    if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3426 3427
        ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
                              lpTpm ? &lpTpm->rcExclude : NULL );
3428
    MENU_ExitTracking(hWnd, TRUE);
3429

Alexandre Julliard's avatar
Alexandre Julliard committed
3430
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3431 3432
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3433
/**********************************************************************
3434 3435 3436 3437 3438
 *           TrackPopupMenu   (USER32.@)
 *
 * Like the win32 API, the function return the command ID only if the
 * flag TPM_RETURNCMD is on.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
3439
 */
3440 3441
BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
                            INT nReserved, HWND hWnd, const RECT *lpRect )
Alexandre Julliard's avatar
Alexandre Julliard committed
3442
{
3443
    return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
3444
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3445

Alexandre Julliard's avatar
Alexandre Julliard committed
3446 3447
/***********************************************************************
 *           PopupMenuWndProc
Alexandre Julliard's avatar
Alexandre Julliard committed
3448 3449
 *
 * NOTE: Windows has totally different (and undocumented) popup wndproc.
Alexandre Julliard's avatar
Alexandre Julliard committed
3450
 */
3451
LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3452
{
3453
    TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3454

Alexandre Julliard's avatar
Alexandre Julliard committed
3455 3456 3457 3458
    switch(message)
    {
    case WM_CREATE:
	{
3459
	    CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3460
	    SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3461
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3462 3463 3464
	}

    case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3465
        return MA_NOACTIVATE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3466 3467 3468

    case WM_PAINT:
	{
3469 3470
	    PAINTSTRUCT ps;
	    BeginPaint( hwnd, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
3471
	    MENU_DrawPopupMenu( hwnd, ps.hdc,
3472
                                (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3473
	    EndPaint( hwnd, &ps );
3474
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3475
	}
3476 3477 3478 3479 3480 3481 3482 3483

    case WM_PRINTCLIENT:
	{
	    MENU_DrawPopupMenu( hwnd, (HDC)wParam,
                                (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
            return 0;
        }

Alexandre Julliard's avatar
Alexandre Julliard committed
3484
    case WM_ERASEBKGND:
3485
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3486

Alexandre Julliard's avatar
Alexandre Julliard committed
3487
    case WM_DESTROY:
3488
        /* zero out global pointer in case resident popup window was destroyed. */
3489 3490 3491 3492
        if (hwnd == top_popup) {
            top_popup = 0;
            top_popup_hmenu = NULL;
        }
3493
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3494

Alexandre Julliard's avatar
Alexandre Julliard committed
3495 3496 3497 3498
    case WM_SHOWWINDOW:

	if( wParam )
	{
3499
            if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
3500 3501
	}
	else
3502
            SetWindowLongPtrW( hwnd, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3503 3504 3505
	break;

    case MM_SETMENUHANDLE:
3506
        SetWindowLongPtrW( hwnd, 0, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3507
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3508 3509

    case MM_GETMENUHANDLE:
3510
    case MN_GETHMENU:
3511
        return GetWindowLongPtrW( hwnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3512

Alexandre Julliard's avatar
Alexandre Julliard committed
3513
    default:
3514
        return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3515
    }
3516
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3517
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3518

Alexandre Julliard's avatar
Alexandre Julliard committed
3519

Alexandre Julliard's avatar
Alexandre Julliard committed
3520 3521 3522 3523 3524
/***********************************************************************
 *           MENU_GetMenuBarHeight
 *
 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
 */
3525 3526
UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
                              INT orgX, INT orgY )
Alexandre Julliard's avatar
Alexandre Julliard committed
3527
{
3528 3529
    HDC hdc;
    RECT rectBar;
Alexandre Julliard's avatar
Alexandre Julliard committed
3530 3531
    LPPOPUPMENU lppop;

3532
    TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3533

3534
    if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3535

3536
    hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3537
    SelectObject( hdc, get_menu_font(FALSE));
3538
    SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3539
    MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3540
    ReleaseDC( hwnd, hdc );
3541
    return lppop->Height;
Alexandre Julliard's avatar
Alexandre Julliard committed
3542 3543 3544
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3545
/*******************************************************************
3546
 *         ChangeMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3547
 */
3548 3549
BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3550
{
3551
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3552
    if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3553
                                                 id, data );
3554 3555
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
Alexandre Julliard's avatar
Alexandre Julliard committed
3556
                                                id, data );
3557
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3558 3559 3560
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3561
    return InsertMenuA( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3562 3563 3564 3565
}


/*******************************************************************
3566
 *         ChangeMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3567
 */
3568 3569
BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3570
{
3571
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3572
    if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3573
                                                 id, data );
3574 3575
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
Alexandre Julliard's avatar
Alexandre Julliard committed
3576
                                                id, data );
3577
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3578 3579 3580
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3581
    return InsertMenuW( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3582
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3583 3584


Alexandre Julliard's avatar
Alexandre Julliard committed
3585
/*******************************************************************
3586
 *         CheckMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3587
 */
3588
DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3589
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3590
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3591
    DWORD ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3592 3593

    if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3594 3595 3596
    ret = item->fState & MF_CHECKED;
    if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
    else item->fState &= ~MF_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
3597
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3598 3599 3600
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3601
/**********************************************************************
3602
 *         EnableMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3603
 */
3604
BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3605
{
3606
    UINT    oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3607
    MENUITEM *item;
3608
    POPUPMENU *menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3609

3610
    TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3611

3612
    /* Get the Popupmenu to access the owner menu */
3613
    if (!(menu = MENU_GetMenu(hMenu)))
3614 3615
	return (UINT)-1;

Alexandre Julliard's avatar
Alexandre Julliard committed
3616
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3617
	return (UINT)-1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3618 3619 3620

    oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
    item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3621

3622 3623 3624 3625
    /* If the close item in the system menu change update the close button */
    if((item->wID == SC_CLOSE) && (oldflags != wFlags))
    {
	if (menu->hSysMenuOwner != 0)
3626
	{
3627
            RECT rc;
3628
	    POPUPMENU* parentMenu;
3629

3630 3631 3632
	    /* Get the parent menu to access*/
	    if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
		return (UINT)-1;
3633

3634 3635 3636 3637 3638
            /* Refresh the frame to reflect the change */
            GetWindowRect(parentMenu->hWnd, &rc);
            MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
            rc.bottom = 0;
            RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3639
	}
3640
    }
3641

Alexandre Julliard's avatar
Alexandre Julliard committed
3642
    return oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3643 3644 3645
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3646
/*******************************************************************
3647
 *         GetMenuStringA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3648
 */
3649
INT WINAPI GetMenuStringA(
3650 3651 3652 3653
	HMENU hMenu,	/* [in] menuhandle */
	UINT wItemID,	/* [in] menu item (dep. on wFlags) */
	LPSTR str,	/* [out] outbuffer. If NULL, func returns entry length*/
	INT nMaxSiz,	/* [in] length of buffer. if 0, func returns entry len*/
3654
	UINT wFlags	/* [in] MF_ flags */
3655
) {
Alexandre Julliard's avatar
Alexandre Julliard committed
3656
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3657

3658
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3659
    if (str && nMaxSiz) str[0] = '\0';
3660 3661 3662 3663
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
3664
    if (!item->text) return 0;
3665
    if (!str || !nMaxSiz) return strlenW(item->text);
3666 3667
    if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
        str[nMaxSiz-1] = 0;
3668
    TRACE("returning %s\n", debugstr_a(str));
Alexandre Julliard's avatar
Alexandre Julliard committed
3669
    return strlen(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3670 3671 3672
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3673
/*******************************************************************
3674
 *         GetMenuStringW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3675
 */
3676 3677
INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
                               LPWSTR str, INT nMaxSiz, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3678 3679 3680
{
    MENUITEM *item;

3681
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3682
    if (str && nMaxSiz) str[0] = '\0';
3683 3684 3685 3686 3687 3688 3689 3690 3691
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
    if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
    if( !(item->text)) {
        str[0] = 0;
        return 0;
    }
3692
    lstrcpynW( str, item->text, nMaxSiz );
3693
    TRACE("returning %s\n", debugstr_w(str));
3694
    return strlenW(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3695 3696 3697
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3698
/**********************************************************************
3699
 *         HiliteMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3700
 */
3701 3702
BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
                                UINT wHilite )
Alexandre Julliard's avatar
Alexandre Julliard committed
3703
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3704
    LPPOPUPMENU menu;
3705
    TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
Alexandre Julliard's avatar
Alexandre Julliard committed
3706
    if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3707
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3708
    if (menu->FocusedItem == wItemID) return TRUE;
3709
    MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3710
    MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3711
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3712 3713 3714
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3715
/**********************************************************************
3716
 *         GetMenuState    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3717
 */
3718
UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3719
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3720
    MENUITEM *item;
3721
    TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3722
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3723
    debug_print_menuitem ("  item: ", item, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
3724
    if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
3725
    {
3726
	POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3727
	if (!menu) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3728
	else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
Alexandre Julliard's avatar
Alexandre Julliard committed
3729
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3730
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
3731
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3732 3733 3734 3735
	/* We used to (from way back then) mask the result to 0xff.  */
	/* I don't know why and it seems wrong as the documented */
	/* return flag MF_SEPARATOR is outside that mask.  */
	return (item->fType | item->fState);
Alexandre Julliard's avatar
Alexandre Julliard committed
3736
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3737 3738 3739
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3740
/**********************************************************************
3741
 *         GetMenuItemCount    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3742
 */
3743
INT WINAPI GetMenuItemCount( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3744
{
3745 3746
    LPPOPUPMENU	menu = MENU_GetMenu(hMenu);
    if (!menu) return -1;
3747
    TRACE("(%p) returning %d\n", hMenu, menu->nItems );
Alexandre Julliard's avatar
Alexandre Julliard committed
3748 3749 3750
    return menu->nItems;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3751 3752

/**********************************************************************
3753
 *         GetMenuItemID    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3754
 */
3755
UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
3756
{
3757
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
3758

3759
    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3760 3761
    if (lpmi->fType & MF_POPUP) return -1;
    return lpmi->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
3762

3763
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3764

Alexandre Julliard's avatar
Alexandre Julliard committed
3765

3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791
/**********************************************************************
 *         MENU_mnu2mnuii
 *
 * Uses flags, id and text ptr, passed by InsertMenu() and
 * ModifyMenu() to setup a MenuItemInfo structure.
 */
static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
        LPMENUITEMINFOW pmii)
{
    ZeroMemory( pmii, sizeof( MENUITEMINFOW));
    pmii->cbSize = sizeof( MENUITEMINFOW);
    pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
    /* setting bitmap clears text and vice versa */
    if( IS_STRING_ITEM(flags)) {
        pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
        if( !str)
            flags |= MF_SEPARATOR;
        /* Item beginning with a backspace is a help item */
        /* FIXME: wrong place, this is only true in win16 */
        else if( *str == '\b') {
            flags |= MF_HELP;
            str++;
        }
        pmii->dwTypeData = (LPWSTR)str;
    } else if( flags & MFT_BITMAP){
        pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3792
        pmii->hbmpItem = ULongToHandle(LOWORD(str));
3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808
    }
    if( flags & MF_OWNERDRAW){
        pmii->fMask |= MIIM_DATA;
        pmii->dwItemData = (ULONG_PTR) str;
    }
    if( flags & MF_POPUP) {
        pmii->fMask |= MIIM_SUBMENU;
        pmii->hSubMenu = (HMENU)id;
    }
    if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
    pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
    pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
    pmii->wID = (UINT)id;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3809
/*******************************************************************
3810
 *         InsertMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3811
 */
3812
BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3813
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3814
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3815
    MENUITEM *item;
3816
    MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
3817

Alexandre Julliard's avatar
Alexandre Julliard committed
3818
    if (IS_STRING_ITEM(flags) && str)
3819
        TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3820
              hMenu, pos, flags, id, debugstr_w(str) );
3821
    else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3822
               hMenu, pos, flags, id, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3823

Alexandre Julliard's avatar
Alexandre Julliard committed
3824
    if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3825 3826
    MENU_mnu2mnuii( flags, id, str, &mii);
    if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
Alexandre Julliard's avatar
Alexandre Julliard committed
3827
    {
3828
        RemoveMenu( hMenu, pos, flags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3829 3830
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3831

Alexandre Julliard's avatar
Alexandre Julliard committed
3832
    item->hCheckBit = item->hUnCheckBit = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3833
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3834 3835 3836
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3837
/*******************************************************************
3838
 *         InsertMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3839
 */
3840
BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3841
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3842
{
3843
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3844 3845 3846

    if (IS_STRING_ITEM(flags) && str)
    {
3847 3848 3849 3850 3851 3852 3853 3854
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = InsertMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3855 3856
        return ret;
    }
3857
    else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3858 3859 3860 3861
}


/*******************************************************************
3862
 *         AppendMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3863
 */
3864
BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3865
                         UINT_PTR id, LPCSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3866
{
3867
    return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3868 3869 3870 3871
}


/*******************************************************************
3872
 *         AppendMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3873
 */
3874
BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3875
                         UINT_PTR id, LPCWSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3876
{
3877
    return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3878 3879 3880
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3881
/**********************************************************************
3882
 *         RemoveMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3883
 */
3884
BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3885
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3886
    LPPOPUPMENU	menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3887 3888
    MENUITEM *item;

3889
    TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3890
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3891
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3892

Alexandre Julliard's avatar
Alexandre Julliard committed
3893 3894
      /* Remove item */

Alexandre Julliard's avatar
Alexandre Julliard committed
3895 3896
    MENU_FreeItemData( item );

Alexandre Julliard's avatar
Alexandre Julliard committed
3897 3898
    if (--menu->nItems == 0)
    {
3899
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
3900
        menu->items = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3901 3902 3903 3904 3905
    }
    else
    {
	while(nPos < menu->nItems)
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
3906 3907
	    *item = *(item+1);
	    item++;
Alexandre Julliard's avatar
Alexandre Julliard committed
3908 3909
	    nPos++;
	}
3910
        menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
Alexandre Julliard's avatar
Alexandre Julliard committed
3911
                                   menu->nItems * sizeof(MENUITEM) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3912 3913
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3914 3915 3916
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3917
/**********************************************************************
3918
 *         DeleteMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3919
 */
3920
BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3921
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3922 3923
    MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
    if (!item) return FALSE;
3924
    if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3925
      /* nPos is now the position of the item */
3926
    RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
Alexandre Julliard's avatar
Alexandre Julliard committed
3927 3928 3929 3930
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3931
/*******************************************************************
3932
 *         ModifyMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3933
 */
3934
BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3935
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3936
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3937
    MENUITEM *item;
3938
    MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
3939 3940

    if (IS_STRING_ITEM(flags))
3941
        TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3942
    else
3943
        TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3944

Alexandre Julliard's avatar
Alexandre Julliard committed
3945
    if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3946
    MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3947 3948
    MENU_mnu2mnuii( flags, id, str, &mii);
    return SetMenuItemInfo_common( item, &mii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3949 3950 3951 3952
}


/*******************************************************************
3953
 *         ModifyMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3954
 */
3955
BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3956
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3957
{
3958
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3959

Alexandre Julliard's avatar
Alexandre Julliard committed
3960 3961
    if (IS_STRING_ITEM(flags) && str)
    {
3962 3963 3964 3965 3966 3967 3968 3969
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3970 3971
        return ret;
    }
3972
    else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3973 3974 3975
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3976
/**********************************************************************
3977
 *         CreatePopupMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3978
 */
3979
HMENU WINAPI CreatePopupMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3980
{
3981
    HMENU hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3982 3983
    POPUPMENU *menu;

3984
    if (!(hmenu = CreateMenu())) return 0;
3985
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3986
    menu->wFlags |= MF_POPUP;
3987
    menu->bTimeToHide = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3988
    return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3989 3990 3991 3992
}


/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
3993 3994
 *         GetMenuCheckMarkDimensions    (USER.417)
 *         GetMenuCheckMarkDimensions    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3995
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3996
DWORD WINAPI GetMenuCheckMarkDimensions(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3997
{
3998
    return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3999 4000 4001
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4002
/**********************************************************************
4003
 *         SetMenuItemBitmaps    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4004
 */
4005 4006
BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
                                    HBITMAP hNewUnCheck, HBITMAP hNewCheck)
Alexandre Julliard's avatar
Alexandre Julliard committed
4007
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4008
    MENUITEM *item;
4009

Alexandre Julliard's avatar
Alexandre Julliard committed
4010
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4011 4012 4013

    if (!hNewCheck && !hNewUnCheck)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
4014
	item->fState &= ~MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
4015 4016 4017
    }
    else  /* Install new bitmaps */
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
4018 4019 4020
	item->hCheckBit = hNewCheck;
	item->hUnCheckBit = hNewUnCheck;
	item->fState |= MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
4021 4022
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4023 4024 4025
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4026
/**********************************************************************
4027
 *         CreateMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4028
 */
4029
HMENU WINAPI CreateMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4030
{
4031
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4032
    LPPOPUPMENU menu;
4033

4034
    if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4035
    menu->FocusedItem = NO_SELECTED_ITEM;
4036
    menu->bTimeToHide = FALSE;
4037

4038 4039
    if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );

4040
    TRACE("return %p\n", hMenu );
4041

Alexandre Julliard's avatar
Alexandre Julliard committed
4042
    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4043 4044 4045
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4046
/**********************************************************************
4047
 *         DestroyMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4048
 */
4049
BOOL WINAPI DestroyMenu( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4050
{
4051
    LPPOPUPMENU lppop;
Alexandre Julliard's avatar
Alexandre Julliard committed
4052

4053
    TRACE("(%p)\n", hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
4054

4055 4056
    if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
    if (lppop == OBJ_OTHER_PROCESS) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4057

4058 4059 4060 4061 4062 4063
    /* DestroyMenu should not destroy system menu popup owner */
    if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
    {
        DestroyWindow( lppop->hWnd );
        lppop->hWnd = 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4064

4065 4066 4067 4068 4069
    if (lppop->items) /* recursively destroy submenus */
    {
        int i;
        MENUITEM *item = lppop->items;
        for (i = lppop->nItems; i > 0; i--, item++)
4070
        {
4071 4072
            if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
            MENU_FreeItemData( item );
4073
        }
4074
        HeapFree( GetProcessHeap(), 0, lppop->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
4075
    }
4076
    HeapFree( GetProcessHeap(), 0, lppop );
4077
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4078 4079
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4080

Alexandre Julliard's avatar
Alexandre Julliard committed
4081
/**********************************************************************
4082
 *         GetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4083
 */
4084
HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
Alexandre Julliard's avatar
Alexandre Julliard committed
4085
{
4086
    WND *wndPtr = WIN_GetPtr( hWnd );
4087
    HMENU retvalue = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4088

4089
    if (wndPtr == WND_DESKTOP) return 0;
4090 4091 4092 4093 4094
    if (wndPtr == WND_OTHER_PROCESS)
    {
        if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
    }
    else if (wndPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
4095
    {
4096
	if (wndPtr->hSysMenu && bRevert)
Alexandre Julliard's avatar
Alexandre Julliard committed
4097
	{
4098 4099
            DestroyMenu(wndPtr->hSysMenu);
            wndPtr->hSysMenu = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4100 4101 4102
	}

	if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4103
	    wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
4104 4105

	if( wndPtr->hSysMenu )
4106
        {
4107
	    POPUPMENU *menu;
4108
	    retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4109 4110 4111

	    /* Store the dummy sysmenu handle to facilitate the refresh */
	    /* of the close button if the SC_CLOSE item change */
4112 4113
	    menu = MENU_GetMenu(retvalue);
	    if ( menu )
4114
	       menu->hSysMenuOwner = wndPtr->hSysMenu;
4115
        }
4116
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
4117
    }
4118
    return bRevert ? 0 : retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
4119 4120
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4121

Alexandre Julliard's avatar
Alexandre Julliard committed
4122
/*******************************************************************
4123
 *         SetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4124
 */
4125
BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4126
{
4127
    WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
4128

4129
    if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
Alexandre Julliard's avatar
Alexandre Julliard committed
4130
    {
4131
	if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4132
	wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4133
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
4134 4135 4136
	return TRUE;
    }
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4137 4138
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4139

Alexandre Julliard's avatar
Alexandre Julliard committed
4140
/**********************************************************************
4141
 *         GetMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4142
 */
4143
HMENU WINAPI GetMenu( HWND hWnd )
4144
{
4145
    HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4146
    TRACE("for %p returning %p\n", hWnd, retvalue);
4147
    return retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
4148 4149
}

4150 4151 4152 4153 4154
/**********************************************************************
 *         GetMenuBarInfo    (USER32.@)
 */
BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
{
4155
    FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4156 4157
    return FALSE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4158

Alexandre Julliard's avatar
Alexandre Julliard committed
4159
/**********************************************************************
4160 4161 4162 4163
 *         MENU_SetMenu
 *
 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
 * SetWindowPos call that would result if SetMenu were called directly.
Alexandre Julliard's avatar
Alexandre Julliard committed
4164
 */
4165
BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4166
{
4167
    TRACE("(%p, %p);\n", hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
4168

4169 4170
    if (hMenu && !IsMenu(hMenu))
    {
4171
        WARN("hMenu %p is not a menu handle\n", hMenu);
4172
        return FALSE;
4173
    }
4174 4175
    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return FALSE;
4176

4177
    hWnd = WIN_GetFullHandle( hWnd );
4178 4179
    if (GetCapture() == hWnd)
        set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
Alexandre Julliard's avatar
Alexandre Julliard committed
4180

4181 4182 4183
    if (hMenu != 0)
    {
        LPPOPUPMENU lpmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4184

4185
        if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4186

4187 4188
        lpmenu->hWnd = hWnd;
        lpmenu->Height = 0;  /* Make sure we recalculate the size */
Alexandre Julliard's avatar
Alexandre Julliard committed
4189
    }
4190
    SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4191
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4192 4193 4194
}


4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207
/**********************************************************************
 *         SetMenu    (USER32.@)
 */
BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
{   
    if(!MENU_SetMenu(hWnd, hMenu))
        return FALSE;
 
    SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4208

Alexandre Julliard's avatar
Alexandre Julliard committed
4209
/**********************************************************************
4210
 *         GetSubMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4211
 */
4212
HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
4213
{
4214
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
4215

4216
    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4217 4218
    if (!(lpmi->fType & MF_POPUP)) return 0;
    return lpmi->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4219 4220 4221
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4222
/**********************************************************************
4223
 *         DrawMenuBar    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4224
 */
4225
BOOL WINAPI DrawMenuBar( HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
4226 4227
{
    LPPOPUPMENU lppop;
4228
    HMENU hMenu = GetMenu(hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
4229

4230 4231
    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return FALSE;
4232 4233 4234 4235 4236 4237 4238
    if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;

    lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
    lppop->hwndOwner = hWnd;
    SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4239 4240
}

4241 4242
/***********************************************************************
 *           DrawMenuBarTemp   (USER32.@)
4243 4244
 *
 * UNDOCUMENTED !!
4245
 *
4246 4247 4248
 * called by W98SE desk.cpl Control Panel Applet
 *
 * Not 100% sure about the param names, but close.
4249
 */
4250
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4251
{
4252 4253 4254
    LPPOPUPMENU lppop;
    UINT i,retvalue;
    HFONT hfontOld = 0;
4255 4256 4257
    BOOL flat_menu = FALSE;

    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4258 4259 4260 4261 4262

    if (!hMenu)
        hMenu = GetMenu(hwnd);

    if (!hFont)
4263
        hFont = get_menu_font(FALSE);
4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280

    lppop = MENU_GetMenu( hMenu );
    if (lppop == NULL || lprect == NULL)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);

    hfontOld = SelectObject( hDC, hFont);

    if (lppop->Height == 0)
        MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);

    lprect->bottom = lprect->top + lppop->Height;

4281
    FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4282

4283 4284 4285
    SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
    MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
    LineTo( hDC, lprect->right, lprect->bottom );
4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302

    if (lppop->nItems == 0)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    for (i = 0; i < lppop->nItems; i++)
    {
        MENU_DrawMenuItem( hwnd, hMenu, hwnd,
                           hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
    }
    retvalue = lppop->Height;

END:
    if (hfontOld) SelectObject (hDC, hfontOld);
    return retvalue;
4303
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4304

Alexandre Julliard's avatar
Alexandre Julliard committed
4305
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
4306 4307
 *           EndMenu   (USER.187)
 *           EndMenu   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4308
 */
4309
BOOL WINAPI EndMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4310
{
4311
    /* if we are in the menu code, and it is active */
4312
    if (!fEndMenu && top_popup)
4313 4314 4315 4316 4317 4318 4319 4320
    {
	/* terminate the menu handling code */
        fEndMenu = TRUE;

	/* needs to be posted to wakeup the internal menu handler */
	/* which will now terminate the menu, in the event that */
	/* the main window was minimized, or lost focus, so we */
	/* don't end up with an orphaned menu */
4321
        PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4322
    }
4323
    return fEndMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4324 4325 4326
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4327
/*****************************************************************
4328
 *        LoadMenuA   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4329
 */
4330
HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
4331
{
4332
    HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
4333
    if (!hrsrc) return 0;
4334
    return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
4335 4336 4337 4338
}


/*****************************************************************
4339
 *        LoadMenuW   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4340
 */
4341
HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
4342
{
4343
    HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
4344
    if (!hrsrc) return 0;
4345
    return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
4346 4347 4348
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4349
/**********************************************************************
4350
 *	    LoadMenuIndirectW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4351
 */
4352
HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
4353
{
4354
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4355
    WORD version, offset;
4356
    LPCSTR p = template;
Alexandre Julliard's avatar
Alexandre Julliard committed
4357 4358 4359

    version = GET_WORD(p);
    p += sizeof(WORD);
4360
    TRACE("%p, ver %d\n", template, version );
Alexandre Julliard's avatar
Alexandre Julliard committed
4361
    switch (version)
4362
    {
4363
      case 0: /* standard format is version of 0 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4364 4365
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
4366
	if (!(hMenu = CreateMenu())) return 0;
4367
	if (!MENU_ParseResource( p, hMenu ))
Alexandre Julliard's avatar
Alexandre Julliard committed
4368
	  {
4369
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4370 4371 4372
	    return 0;
	  }
	return hMenu;
4373
      case 1: /* extended format is version of 1 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4374 4375
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
4376
	if (!(hMenu = CreateMenu())) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4377 4378
	if (!MENUEX_ParseResource( p, hMenu))
	  {
4379
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4380 4381 4382 4383
	    return 0;
	  }
	return hMenu;
      default:
4384
        ERR("version %d not supported.\n", version);
Alexandre Julliard's avatar
Alexandre Julliard committed
4385
        return 0;
4386
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4387 4388 4389 4390
}


/**********************************************************************
4391
 *	    LoadMenuIndirectA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4392
 */
4393
HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
4394
{
4395
    return LoadMenuIndirectW( template );
Alexandre Julliard's avatar
Alexandre Julliard committed
4396 4397 4398
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4399
/**********************************************************************
4400
 *		IsMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4401
 */
4402
BOOL WINAPI IsMenu(HMENU hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
4403
{
4404
    LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4405 4406 4407 4408 4409 4410 4411

    if (!menu)
    {
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4412 4413
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4414
/**********************************************************************
4415
 *		GetMenuItemInfo_common
Alexandre Julliard's avatar
Alexandre Julliard committed
4416 4417
 */

4418
static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4419
					LPMENUITEMINFOW lpmii, BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
4420
{
4421
    MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4422

4423
    debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4424

4425 4426 4427 4428
    if (!menu) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return FALSE;
    }
4429 4430 4431
    
    if( lpmii->fMask & MIIM_TYPE) {
        if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4432 4433
            WARN("invalid combination of fMask bits used\n");
            /* this does not happen on Win9x/ME */
4434 4435 4436
            SetLastError( ERROR_INVALID_PARAMETER);
            return FALSE;
        }
4437
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4438
        if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4439
	lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4440 4441
        if( lpmii->fType & MFT_BITMAP) {
	    lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4442
	    lpmii->cch = 0;
4443
        } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4444
            /* this does not happen on Win9x/ME */
4445 4446 4447
	    lpmii->dwTypeData = 0;
	    lpmii->cch = 0;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4448
    }
4449

4450
    /* copy the text string */
4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477
    if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
         if( !menu->text ) {
                if(lpmii->dwTypeData && lpmii->cch) {
                    lpmii->cch = 0;
                    if( unicode)
                        *((WCHAR *)lpmii->dwTypeData) = 0;
                    else
                        *((CHAR *)lpmii->dwTypeData) = 0;
                }
         } else {
            int len;
            if (unicode)
            {
                len = strlenW(menu->text);
                if(lpmii->dwTypeData && lpmii->cch)
                    lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
            }
            else
            {
                len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
                        0, NULL, NULL ) - 1;
                if(lpmii->dwTypeData && lpmii->cch)
                    if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
                            (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
                        ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
            }
            /* if we've copied a substring we return its length */
4478
            if(lpmii->dwTypeData && lpmii->cch)
4479 4480 4481 4482
                if (lpmii->cch <= len + 1)
                    lpmii->cch--;
                else
                    lpmii->cch = len;
4483 4484 4485
            else {
                /* return length of string */
                /* not on Win9x/ME if fType & MFT_BITMAP */
4486
                lpmii->cch = len;
4487
            }
4488
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4489
    }
4490 4491

    if (lpmii->fMask & MIIM_FTYPE)
4492
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4493 4494 4495 4496

    if (lpmii->fMask & MIIM_BITMAP)
	lpmii->hbmpItem = menu->hbmpItem;

Alexandre Julliard's avatar
Alexandre Julliard committed
4497
    if (lpmii->fMask & MIIM_STATE)
4498
	lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
Alexandre Julliard's avatar
Alexandre Julliard committed
4499

Alexandre Julliard's avatar
Alexandre Julliard committed
4500 4501
    if (lpmii->fMask & MIIM_ID)
	lpmii->wID = menu->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
4502

Alexandre Julliard's avatar
Alexandre Julliard committed
4503 4504
    if (lpmii->fMask & MIIM_SUBMENU)
	lpmii->hSubMenu = menu->hSubMenu;
4505 4506 4507 4508 4509
    else {
        /* hSubMenu is always cleared 
         * (not on Win9x/ME ) */
        lpmii->hSubMenu = 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4510

Alexandre Julliard's avatar
Alexandre Julliard committed
4511 4512 4513
    if (lpmii->fMask & MIIM_CHECKMARKS) {
	lpmii->hbmpChecked = menu->hCheckBit;
	lpmii->hbmpUnchecked = menu->hUnCheckBit;
Alexandre Julliard's avatar
Alexandre Julliard committed
4514
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4515 4516
    if (lpmii->fMask & MIIM_DATA)
	lpmii->dwItemData = menu->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
4517 4518 4519 4520 4521

  return TRUE;
}

/**********************************************************************
4522
 *		GetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4523
 */
4524 4525
BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOA lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4526
{
4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540
    BOOL ret;
    MENUITEMINFOA mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos,
                                    (LPMENUITEMINFOW)&mii, FALSE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
4541 4542 4543
}

/**********************************************************************
4544
 *		GetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4545
 */
4546 4547
BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOW lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4548
{
4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561
    BOOL ret;
    MENUITEMINFOW mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
4562 4563
}

4564 4565

/* set a menu item text from a ASCII or Unicode string */
4566
static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584
{
    if (!text)
        menu->text = NULL;
    else if (unicode)
    {
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
            strcpyW( menu->text, text );
    }
    else
    {
        LPCSTR str = (LPCSTR)text;
        int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
            MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
    }
}


4585 4586 4587 4588 4589 4590 4591 4592 4593
/**********************************************************************
 *		MENU_depth
 *
 * detect if there are loops in the menu tree (or the depth is too large)
 */
static int MENU_depth( POPUPMENU *pmenu, int depth)
{
    int i;
    MENUITEM *item;
4594
    int subdepth;
4595 4596 4597 4598

    depth++;
    if( depth > MAXMENUDEPTH) return depth;
    item = pmenu->items;
4599 4600 4601
    subdepth = depth;
    for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
        POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4602 4603
        if( psubmenu){
            int bdepth = MENU_depth( psubmenu, depth);
4604
            if( bdepth > subdepth) subdepth = bdepth;
4605
        }
4606 4607
        if( subdepth > MAXMENUDEPTH)
            TRACE("<- hmenu %p\n", item->hSubMenu);
4608
    }
4609
    return subdepth;
4610 4611 4612
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4613
/**********************************************************************
4614
 *		SetMenuItemInfo_common
4615 4616 4617
 *
 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
 * MIIM_BITMAP and MIIM_STRING flags instead.
Alexandre Julliard's avatar
Alexandre Julliard committed
4618 4619
 */

4620
static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4621
				       const MENUITEMINFOW *lpmii,
4622
				       BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
4623 4624 4625
{
    if (!menu) return FALSE;

4626
    debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4627

4628
    if (lpmii->fMask & MIIM_FTYPE ) {
4629 4630
        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
        menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4631 4632
    }
    if (lpmii->fMask & MIIM_STRING ) {
4633 4634 4635
        /* free the string when used */
        HeapFree(GetProcessHeap(), 0, menu->text);
        set_menu_item_text( menu, lpmii->dwTypeData, unicode );
Alexandre Julliard's avatar
Alexandre Julliard committed
4636
    }
4637

Alexandre Julliard's avatar
Alexandre Julliard committed
4638
    if (lpmii->fMask & MIIM_STATE)
4639 4640 4641
         /* Other menu items having MFS_DEFAULT are not converted
           to normal items */
         menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
Alexandre Julliard's avatar
Alexandre Julliard committed
4642 4643 4644 4645

    if (lpmii->fMask & MIIM_ID)
	menu->wID = lpmii->wID;

Alexandre Julliard's avatar
Alexandre Julliard committed
4646
    if (lpmii->fMask & MIIM_SUBMENU) {
Alexandre Julliard's avatar
Alexandre Julliard committed
4647
	menu->hSubMenu = lpmii->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4648
	if (menu->hSubMenu) {
4649
	    POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4650
	    if (subMenu) {
4651 4652 4653 4654 4655
                if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
                    ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
                    menu->hSubMenu = 0;
                    return FALSE;
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
4656 4657
		subMenu->wFlags |= MF_POPUP;
		menu->fType |= MF_POPUP;
4658
	    } else {
4659 4660 4661
                SetLastError( ERROR_INVALID_PARAMETER);
                return FALSE;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
4662 4663 4664 4665
	}
	else
	    menu->fType &= ~MF_POPUP;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4666 4667 4668 4669 4670 4671 4672 4673 4674

    if (lpmii->fMask & MIIM_CHECKMARKS)
    {
	menu->hCheckBit = lpmii->hbmpChecked;
	menu->hUnCheckBit = lpmii->hbmpUnchecked;
    }
    if (lpmii->fMask & MIIM_DATA)
	menu->dwItemData = lpmii->dwItemData;

4675 4676 4677
    if (lpmii->fMask & MIIM_BITMAP)
	menu->hbmpItem = lpmii->hbmpItem;

4678 4679 4680
    if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
        menu->fType |= MFT_SEPARATOR;

4681
    debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
4682 4683
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4684

4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706
/**********************************************************************
 *		MENU_NormalizeMenuItemInfoStruct
 *
 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
 * check, copy and extend the MENUITEMINFO struct from the version that the application
 * supplied to the version used by wine source. */
static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
                                              MENUITEMINFOW *pmii_out )
{
    /* do we recognize the size? */
    if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
            pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    /* copy the fields that we have */
    memcpy( pmii_out, pmii_in, pmii_in->cbSize);
    /* if the hbmpItem member is missing then extend */
    if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
        pmii_out->cbSize = sizeof( MENUITEMINFOW);
        pmii_out->hbmpItem = NULL;
    }
4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722
    /* test for invalid bit combinations */
    if( (pmii_out->fMask & MIIM_TYPE &&
         pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
        (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
        WARN("invalid combination of fMask bits used\n");
        /* this does not happen on Win9x/ME */
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    /* convert old style (MIIM_TYPE) to the new */
    if( pmii_out->fMask & MIIM_TYPE){
        pmii_out->fMask |= MIIM_FTYPE;
        if( IS_STRING_ITEM(pmii_out->fType)){
            pmii_out->fMask |= MIIM_STRING;
        } else if( (pmii_out->fType) & MFT_BITMAP){
            pmii_out->fMask |= MIIM_BITMAP;
4723
            pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4724 4725
        }
    }
4726 4727 4728
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4729
/**********************************************************************
4730
 *		SetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4731
 */
4732
BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4733
                                 const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4734
{
4735
    MENUITEMINFOW mii;
4736 4737 4738

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

4739
    if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4740

4741
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4742
                                  &mii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4743 4744 4745
}

/**********************************************************************
4746
 *		SetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4747
 */
4748 4749
BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4750
{
4751
    MENUITEMINFOW mii;
4752 4753 4754

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

4755
    if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4756 4757
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
                &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4758 4759 4760
}

/**********************************************************************
4761
 *		SetMenuDefaultItem    (USER32.@)
4762
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4763
 */
4764
BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
4765
{
4766 4767 4768
	UINT i;
	POPUPMENU *menu;
	MENUITEM *item;
4769

4770
	TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
Alexandre Julliard's avatar
Alexandre Julliard committed
4771

4772
	if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4773

4774 4775 4776 4777 4778 4779
	/* reset all default-item flags */
	item = menu->items;
	for (i = 0; i < menu->nItems; i++, item++)
	{
	    item->fState &= ~MFS_DEFAULT;
	}
4780

4781 4782 4783 4784 4785
	/* no default item */
	if ( -1 == uItem)
	{
	    return TRUE;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
4786

4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803
	item = menu->items;
	if ( bypos )
	{
	    if ( uItem >= menu->nItems ) return FALSE;
	    item[uItem].fState |= MFS_DEFAULT;
	    return TRUE;
	}
	else
	{
	    for (i = 0; i < menu->nItems; i++, item++)
	    {
		if (item->wID == uItem)
		{
		     item->fState |= MFS_DEFAULT;
		     return TRUE;
		}
	    }
4804

4805 4806
	}
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4807 4808
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4809
/**********************************************************************
4810
 *		GetMenuDefaultItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4811
 */
4812
UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
4813
{
4814 4815 4816
	POPUPMENU *menu;
	MENUITEM * item;
	UINT i = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4817

4818
	TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4819

4820
	if (!(menu = MENU_GetMenu(hmenu))) return -1;
4821 4822 4823

	/* find default item */
	item = menu->items;
4824

4825 4826
	/* empty menu */
	if (! item) return -1;
4827

4828 4829 4830 4831 4832
	while ( !( item->fState & MFS_DEFAULT ) )
	{
	    i++; item++;
	    if  (i >= menu->nItems ) return -1;
	}
4833

4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846
	/* default: don't return disabled items */
	if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;

	/* search rekursiv when needed */
	if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
	{
	    UINT ret;
	    ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
	    if ( -1 != ret ) return ret;

	    /* when item not found in submenu, return the popup item */
	}
	return ( bypos ) ? i : item->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
4847 4848 4849

}

Alexandre Julliard's avatar
Alexandre Julliard committed
4850 4851

/**********************************************************************
4852
 *		InsertMenuItemA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4853
 */
4854 4855
BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4856
{
4857
    MENUITEM *item;
4858
    MENUITEMINFOW mii;
4859 4860 4861

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

4862
    if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4863 4864

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4865
    return SetMenuItemInfo_common(item, &mii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4866 4867 4868 4869
}


/**********************************************************************
4870
 *		InsertMenuItemW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4871
 */
4872 4873
BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4874
{
4875
    MENUITEM *item;
4876
    MENUITEMINFOW mii;
4877 4878 4879

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

4880
    if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4881 4882

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4883
    return SetMenuItemInfo_common(item, &mii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4884
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4885 4886

/**********************************************************************
4887
 *		CheckMenuRadioItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4888 4889
 */

4890 4891 4892
BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
				   UINT first, UINT last, UINT check,
				   UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
4893
{
4894 4895 4896 4897
    BOOL done = FALSE;
    UINT i;
    MENUITEM *mi_first = NULL, *mi_check;
    HMENU m_first, m_check;
Alexandre Julliard's avatar
Alexandre Julliard committed
4898

4899 4900 4901
    for (i = first; i <= last; i++)
    {
        UINT pos = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
4902

4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916
        if (!mi_first)
        {
            m_first = hMenu;
            mi_first = MENU_FindItem(&m_first, &pos, bypos);
            if (!mi_first) continue;
            mi_check = mi_first;
            m_check = m_first;
        }
        else
        {
            m_check = hMenu;
            mi_check = MENU_FindItem(&m_check, &pos, bypos);
            if (!mi_check) continue;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4917

4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932
        if (m_first != m_check) continue;
        if (mi_check->fType == MFT_SEPARATOR) continue;

        if (i == check)
        {
            mi_check->fType |= MFT_RADIOCHECK;
            mi_check->fState |= MFS_CHECKED;
            done = TRUE;
        }
        else
        {
            /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
            mi_check->fState &= ~MFS_CHECKED;
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4933

4934
    return done;
Alexandre Julliard's avatar
Alexandre Julliard committed
4935 4936 4937 4938
}


/**********************************************************************
4939
 *		GetMenuItemRect    (USER32.@)
4940
 *
4941 4942
 *      ATTENTION: Here, the returned values in rect are the screen
 *                 coordinates of the item just like if the menu was
4943
 *                 always on the upper left side of the application.
4944
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4945
 */
4946 4947
BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
				 LPRECT rect)
Alexandre Julliard's avatar
Alexandre Julliard committed
4948
{
4949
     POPUPMENU *itemMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4950
     MENUITEM *item;
4951
     HWND referenceHwnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
4952

4953
     TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
Alexandre Julliard's avatar
Alexandre Julliard committed
4954 4955

     item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4956 4957 4958 4959
     referenceHwnd = hwnd;

     if(!hwnd)
     {
4960
	 itemMenu = MENU_GetMenu(hMenu);
4961
	 if (itemMenu == NULL)
4962
	     return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4963

4964
	 if(itemMenu->hWnd == 0)
4965 4966 4967 4968
	     return FALSE;
	 referenceHwnd = itemMenu->hWnd;
     }

4969
     if ((rect == NULL) || (item == NULL))
4970
	 return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4971 4972 4973

     *rect = item->rect;

4974
     MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4975

Alexandre Julliard's avatar
Alexandre Julliard committed
4976 4977 4978
     return TRUE;
}

4979
/**********************************************************************
4980
 *		SetMenuInfo    (USER32.@)
4981 4982 4983
 *
 * FIXME
 *	actually use the items to draw the menu
4984
 *      (recalculate and/or redraw)
4985
 */
4986
static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
4987 4988
{
    POPUPMENU *menu;
4989
    if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
4990

4991 4992
    if (lpmi->fMask & MIM_BACKGROUND)
        menu->hbrBack = lpmi->hbrBack;
4993

4994 4995
    if (lpmi->fMask & MIM_HELPID)
        menu->dwContextHelpID = lpmi->dwContextHelpID;
4996

4997 4998
    if (lpmi->fMask & MIM_MAXHEIGHT)
        menu->cyMax = lpmi->cyMax;
4999

5000 5001
    if (lpmi->fMask & MIM_MENUDATA)
        menu->dwMenuData = lpmi->dwMenuData;
5002

5003 5004
    if (lpmi->fMask & MIM_STYLE)
        menu->dwStyle = lpmi->dwStyle;
5005

5006 5007 5008 5009 5010 5011 5012 5013 5014
    if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
        int i;
        MENUITEM *item = menu->items;
        for( i = menu->nItems; i; i--, item++)
            if( item->fType & MF_POPUP)
                menu_SetMenuInfo( item->hSubMenu, lpmi);
    }
    return TRUE;
}
5015

5016 5017 5018 5019 5020 5021 5022 5023
BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
{
    TRACE("(%p %p)\n", hMenu, lpmi);
    if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
	if( lpmi->fMask & MIM_STYLE) {
	    if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
	    if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
	    if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5024
	}
5025
        return TRUE;
5026
    }
5027
    SetLastError( ERROR_INVALID_PARAMETER);
5028 5029 5030 5031
    return FALSE;
}

/**********************************************************************
5032
 *		GetMenuInfo    (USER32.@)
5033 5034 5035 5036 5037 5038 5039 5040
 *
 *  NOTES
 *	win98/NT5.0
 *
 */
BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
{   POPUPMENU *menu;

5041
    TRACE("(%p %p)\n", hMenu, lpmi);
5042

5043
    if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062
    {

	if (lpmi->fMask & MIM_BACKGROUND)
	    lpmi->hbrBack = menu->hbrBack;

	if (lpmi->fMask & MIM_HELPID)
	    lpmi->dwContextHelpID = menu->dwContextHelpID;

	if (lpmi->fMask & MIM_MAXHEIGHT)
	    lpmi->cyMax = menu->cyMax;

	if (lpmi->fMask & MIM_MENUDATA)
	    lpmi->dwMenuData = menu->dwMenuData;

	if (lpmi->fMask & MIM_STYLE)
	    lpmi->dwStyle = menu->dwStyle;

	return TRUE;
    }
5063
    SetLastError( ERROR_INVALID_PARAMETER);
5064 5065 5066
    return FALSE;
}

5067 5068

/**********************************************************************
5069
 *         SetMenuContextHelpId    (USER32.@)
5070
 */
5071
BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5072
{
5073 5074
    LPPOPUPMENU menu;

5075
    TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5076

5077
    if ((menu = MENU_GetMenu(hMenu)))
5078 5079 5080 5081 5082
    {
	menu->dwContextHelpID = dwContextHelpID;
	return TRUE;
    }
    return FALSE;
5083 5084
}

5085

5086
/**********************************************************************
5087
 *         GetMenuContextHelpId    (USER32.@)
5088
 */
5089
DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5090
{
5091 5092
    LPPOPUPMENU menu;

5093
    TRACE("(%p)\n", hMenu);
5094

5095
    if ((menu = MENU_GetMenu(hMenu)))
5096 5097 5098 5099
    {
	return menu->dwContextHelpID;
    }
    return 0;
5100
}
5101 5102

/**********************************************************************
5103
 *         MenuItemFromPoint    (USER32.@)
5104
 */
5105
INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5106
{
5107 5108 5109 5110
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT pos;

    /*FIXME: Do we have to handle hWnd here? */
5111 5112
    if (!menu) return -1;
    if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5113
    return pos;
5114
}
5115 5116 5117 5118 5119 5120 5121 5122


/**********************************************************************
 *           translate_accelerator
 */
static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                   BYTE fVirt, WORD key, WORD cmd )
{
5123
    INT mask = 0;
5124 5125 5126 5127
    UINT mesg = 0;

    if (wParam != key) return FALSE;

5128 5129
    if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
    if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5130
    if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5131

5132
    if (message == WM_CHAR || message == WM_SYSCHAR)
5133
    {
5134
        if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5135
        {
5136
            TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5137 5138 5139 5140 5141 5142 5143
            goto found;
        }
    }
    else
    {
        if(fVirt & FVIRTKEY)
        {
5144
            TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5145
                          wParam, 0xff & HIWORD(lParam));
5146

5147 5148 5149 5150 5151 5152 5153 5154 5155
            if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
            TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
        }
        else
        {
            if (!(lParam & 0x01000000))  /* no special_key */
            {
                if ((fVirt & FALT) && (lParam & 0x20000000))
                {                              /* ^^ ALT pressed */
5156
                    TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171
                    goto found;
                }
            }
        }
    }
    return FALSE;

 found:
    if (message == WM_KEYUP || message == WM_SYSKEYUP)
        mesg = 1;
    else
    {
        HMENU hMenu, hSubMenu, hSysMenu;
        UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;

5172
        hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5173
        hSysMenu = get_win_sys_menu( hWnd );
5174 5175 5176 5177 5178 5179 5180

        /* find menu item and ask application to initialize it */
        /* 1. in the system menu */
        hSubMenu = hSysMenu;
        nPos = cmd;
        if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
        {
5181 5182 5183 5184 5185
            if (GetCapture())
                mesg = 2;
            if (!IsWindowEnabled(hWnd))
                mesg = 3;
            else
5186
            {
5187 5188 5189 5190 5191 5192 5193 5194
                SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
                if(hSubMenu != hSysMenu)
                {
                    nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
                    TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
                    SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
                }
                uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5195 5196 5197 5198 5199 5200 5201 5202
            }
        }
        else /* 2. in the window's menu */
        {
            hSubMenu = hMenu;
            nPos = cmd;
            if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
            {
5203 5204 5205 5206 5207
                if (GetCapture())
                    mesg = 2;
                if (!IsWindowEnabled(hWnd))
                    mesg = 3;
                else
5208
                {
5209 5210 5211 5212 5213 5214 5215 5216
                    SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
                    if(hSubMenu != hMenu)
                    {
                        nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
                        TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
                        SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
                    }
                    uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5217 5218 5219 5220
                }
            }
        }

5221
        if (mesg == 0)
5222
        {
5223
            if (uSysStat != (UINT)-1)
5224
            {
5225 5226
                if (uSysStat & (MF_DISABLED|MF_GRAYED))
                    mesg=4;
5227
                else
5228 5229 5230 5231 5232
                    mesg=WM_SYSCOMMAND;
            }
            else
            {
                if (uStat != (UINT)-1)
5233
                {
5234 5235
                    if (IsIconic(hWnd))
                        mesg=5;
5236
                    else
5237 5238 5239 5240 5241 5242
                    {
                        if (uStat & (MF_DISABLED|MF_GRAYED))
                            mesg=6;
                        else
                            mesg=WM_COMMAND;
                    }
5243
                }
5244 5245
                else
                    mesg=WM_COMMAND;
5246 5247 5248 5249 5250 5251 5252
            }
        }
    }

    if( mesg==WM_COMMAND )
    {
        TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5253
        SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5254 5255 5256 5257
    }
    else if( mesg==WM_SYSCOMMAND )
    {
        TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5258
        SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5259 5260 5261
    }
    else
    {
5262
        /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5263 5264 5265
         *   #0: unknown (please report!)
         *   #1: for WM_KEYUP,WM_SYSKEYUP
         *   #2: mouse is captured
5266
         *   #3: window is disabled
5267 5268 5269 5270 5271 5272
         *   #4: it's a disabled system menu option
         *   #5: it's a menu option, but window is iconic
         *   #6: it's a menu option, but disabled
         */
        TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
        if(mesg==0)
5273
            ERR_(accel)(" unknown reason - please report!\n");
5274 5275 5276 5277 5278
    }
    return TRUE;
}

/**********************************************************************
5279
 *      TranslateAcceleratorA     (USER32.@)
5280
 *      TranslateAccelerator      (USER32.@)
5281
 */
5282
INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5283
{
5284
    switch (msg->message)
5285
    {
5286 5287
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
5288
        return TranslateAcceleratorW( hWnd, hAccel, msg );
5289 5290 5291 5292

    case WM_CHAR:
    case WM_SYSCHAR:
        {
5293 5294
            MSG msgW = *msg;
            char ch = LOWORD(msg->wParam);
5295 5296
            WCHAR wch;
            MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5297 5298
            msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
            return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5299 5300 5301
        }

    default:
5302 5303
        return 0;
    }
5304 5305 5306 5307 5308 5309 5310
}

/**********************************************************************
 *      TranslateAcceleratorW     (USER32.@)
 */
INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
{
5311 5312
    ACCEL data[32], *ptr = data;
    int i, count;
5313

5314
    if (!hWnd) return 0;
5315

5316 5317 5318 5319
    if (msg->message != WM_KEYDOWN &&
        msg->message != WM_SYSKEYDOWN &&
        msg->message != WM_CHAR &&
        msg->message != WM_SYSCHAR)
5320 5321
        return 0;

5322
    TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5323
                  hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5324 5325 5326 5327 5328 5329 5330 5331

    if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
    if (count > sizeof(data)/sizeof(data[0]))
    {
        if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
    }
    count = CopyAcceleratorTableW( hAccel, ptr, count );
    for (i = 0; i < count; i++)
5332 5333
    {
        if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5334 5335 5336 5337 5338
                                   ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
            break;
    }
    if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
    return (i < count);
5339
}