menu.c 133 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 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
22 23 24 25 26 27

/*
 * 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.
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
28

29
#include "config.h"
30 31
#include "wine/port.h"

32
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
33
#include <string.h>
34

35
#include "windef.h"
36
#include "winbase.h"
37
#include "wingdi.h"
38
#include "winnls.h"
39
#include "wine/winbase16.h"
40
#include "wine/winuser16.h"
41
#include "wownt32.h"
42
#include "wine/server.h"
43
#include "wine/unicode.h"
44
#include "win.h"
45
#include "controls.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
46
#include "nonclient.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
47
#include "user.h"
48
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
49

50 51
WINE_DEFAULT_DEBUG_CHANNEL(menu);
WINE_DECLARE_DEBUG_CHANNEL(accel);
Alexandre Julliard's avatar
Alexandre Julliard committed
52

Alexandre Julliard's avatar
Alexandre Julliard committed
53 54 55 56 57
/* internal popup menu window messages */

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

Alexandre Julliard's avatar
Alexandre Julliard committed
58
/* Menu item structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
59 60
typedef struct {
    /* ----------- MENUITEMINFO Stuff ----------- */
61
    UINT fType;			/* Item type. */
62
    UINT fState;		/* Item state.  */
63
    UINT_PTR wID;		/* Item id.  */
64
    HMENU hSubMenu;		/* Pop-up menu.  */
65
    HBITMAP hCheckBit;		/* Bitmap when checked.  */
66
    HBITMAP hUnCheckBit;	/* Bitmap when unchecked.  */
67
    LPWSTR text;			/* Item text or bitmap handle.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
68
    DWORD dwItemData;		/* Application defined.  */
69 70
    DWORD dwTypeData;		/* depends on fMask */
    HBITMAP hbmpItem;		/* bitmap in win98 style menus */
Alexandre Julliard's avatar
Alexandre Julliard committed
71
    /* ----------- Wine stuff ----------- */
72 73
    RECT      rect;		/* Item area (relative to menu window) */
    UINT      xTab;		/* X position of text after Tab */
Alexandre Julliard's avatar
Alexandre Julliard committed
74 75 76
} MENUITEM;

/* Popup menu structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
77
typedef struct {
Alexandre Julliard's avatar
Alexandre Julliard committed
78 79 80 81
    WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
    WORD        wMagic;       /* Magic number */
    WORD	Width;        /* Width of the whole menu */
    WORD	Height;       /* Height of the whole menu */
82
    UINT        nItems;       /* Number of items in the menu */
83 84 85
    HWND        hWnd;         /* Window containing the menu */
    MENUITEM    *items;       /* Array of menu items */
    UINT        FocusedItem;  /* Currently focused item */
86
    HWND	hwndOwner;    /* window receiving the messages for ownerdraw */
87
    BOOL        bTimeToHide;  /* Request hiding when receiving a second click in the top-level menu item */
88 89 90 91 92 93
    /* ------------ MENUINFO members ------ */
    DWORD	dwStyle;	/* Extended mennu style */
    UINT	cyMax;		/* max hight of the whole menu, 0 is screen hight */
    HBRUSH	hbrBack;	/* brush for menu background */
    DWORD	dwContextHelpID;
    DWORD	dwMenuData;	/* application defined value */
94
    HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
Alexandre Julliard's avatar
Alexandre Julliard committed
95 96
} POPUPMENU, *LPPOPUPMENU;

Alexandre Julliard's avatar
Alexandre Julliard committed
97 98 99 100 101 102 103 104
/* internal flags for menu tracking */

#define TF_ENDMENU              0x0001
#define TF_SUSPENDPOPUP         0x0002
#define TF_SKIPREMOVE		0x0004

typedef struct
{
105 106 107 108 109
    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
110 111
} MTRACKER;

Alexandre Julliard's avatar
Alexandre Julliard committed
112 113
#define MENU_MAGIC   0x554d  /* 'MU' */

Alexandre Julliard's avatar
Alexandre Julliard committed
114 115 116
#define ITEM_PREV		-1
#define ITEM_NEXT		 1

Alexandre Julliard's avatar
Alexandre Julliard committed
117 118 119 120
  /* Internal MENU_TrackMenu() flags */
#define TPM_INTERNAL		0xF0000000
#define TPM_ENTERIDLEEX	 	0x80000000		/* set owner window for WM_ENTERIDLE */
#define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
121
#define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
122

Alexandre Julliard's avatar
Alexandre Julliard committed
123 124 125
  /* popup menu shade thickness */
#define POPUP_XSHADE		4
#define POPUP_YSHADE		4
Alexandre Julliard's avatar
Alexandre Julliard committed
126

Alexandre Julliard's avatar
Alexandre Julliard committed
127
  /* Space between 2 menu bar items */
Alexandre Julliard's avatar
Alexandre Julliard committed
128
#define MENU_BAR_ITEMS_SPACE 12
Alexandre Julliard's avatar
Alexandre Julliard committed
129

Alexandre Julliard's avatar
Alexandre Julliard committed
130
  /* Minimum width of a tab character */
Alexandre Julliard's avatar
Alexandre Julliard committed
131
#define MENU_TAB_SPACE 8
Alexandre Julliard's avatar
Alexandre Julliard committed
132

Alexandre Julliard's avatar
Alexandre Julliard committed
133
  /* Height of a separator item */
Alexandre Julliard's avatar
Alexandre Julliard committed
134
#define SEPARATOR_HEIGHT 5
Alexandre Julliard's avatar
Alexandre Julliard committed
135

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

Alexandre Julliard's avatar
Alexandre Julliard committed
139 140 141 142
#define MENU_ITEM_TYPE(flags) \
  ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))

#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
143
#define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
144
#define IS_MAGIC_ITEM(text)   (LOWORD((int)text)<12)
Alexandre Julliard's avatar
Alexandre Julliard committed
145

Alexandre Julliard's avatar
Alexandre Julliard committed
146 147
#define IS_SYSTEM_MENU(menu)  \
	(!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
148

Alexandre Julliard's avatar
Alexandre Julliard committed
149 150 151
#define IS_SYSTEM_POPUP(menu) \
	((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)

Alexandre Julliard's avatar
Alexandre Julliard committed
152 153 154 155 156 157
#define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
		   MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
		   MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
		   MF_POPUP | MF_SYSMENU | MF_HELP)
#define STATE_MASK (~TYPE_MASK)

Alexandre Julliard's avatar
Alexandre Julliard committed
158 159
  /* Dimension of the menu bitmaps */
static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
160

161
static HBITMAP hStdMnArrow = 0;
162
static HBITMAP hBmpSysMenu = 0;
163

164 165
static HBRUSH	hShadeBrush = 0;
static HFONT	hMenuFont = 0;
166
static HFONT	hMenuFontBold = 0;
167

168
static HMENU MENU_DefSysPopup = 0;  /* Default system menu popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
169

Alexandre Julliard's avatar
Alexandre Julliard committed
170
/* Use global popup window because there's no way 2 menus can
171
 * be tracked at the same time.  */
172
static HWND top_popup;
Alexandre Julliard's avatar
Alexandre Julliard committed
173

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

177 178
static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );

179
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
180 181 182 183 184 185

/*********************************************************************
 * menu class descriptor
 */
const struct builtin_class_descr MENU_builtin_class =
{
186
    POPUPMENU_CLASS_ATOMA,         /* name */
187
    CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
188 189 190
    NULL,                          /* procA (winproc is Unicode only) */
    PopupMenuWndProc,              /* procW */
    sizeof(HMENU),                 /* extra */
191
    IDC_ARROW,                     /* cursor */
192
    (HBRUSH)(COLOR_MENU+1)         /* brush */
193 194
};

Alexandre Julliard's avatar
Alexandre Julliard committed
195

Alexandre Julliard's avatar
Alexandre Julliard committed
196 197 198 199 200 201
/***********************************************************************
 *           debug_print_menuitem
 *
 * Print a menuitem in readable form.
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
202
#define debug_print_menuitem(pre, mp, post) \
Alexandre Julliard's avatar
Alexandre Julliard committed
203
  if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
Alexandre Julliard's avatar
Alexandre Julliard committed
204

Alexandre Julliard's avatar
Alexandre Julliard committed
205
#define MENUOUT(text) \
206
  DPRINTF("%s%s", (count++ ? "," : ""), (text))
Alexandre Julliard's avatar
Alexandre Julliard committed
207 208 209 210 211 212

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

213
static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
Alexandre Julliard's avatar
Alexandre Julliard committed
214
				    const char *postfix)
Alexandre Julliard's avatar
Alexandre Julliard committed
215
{
216
    TRACE("%s ", prefix);
Alexandre Julliard's avatar
Alexandre Julliard committed
217
    if (mp) {
218
	UINT flags = mp->fType;
219
	int type = MENU_ITEM_TYPE(flags);
220
	DPRINTF( "{ ID=0x%x", mp->wID);
Alexandre Julliard's avatar
Alexandre Julliard committed
221
	if (flags & MF_POPUP)
222
	    DPRINTF( ", Sub=%p", mp->hSubMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
223 224
	if (flags) {
	    int count = 0;
225 226
	    DPRINTF( ", Type=");
	    if (type == MFT_STRING)
Alexandre Julliard's avatar
Alexandre Julliard committed
227
		/* Nothing */ ;
228
	    else if (type == MFT_SEPARATOR)
Alexandre Julliard's avatar
Alexandre Julliard committed
229
		MENUOUT("sep");
230
	    else if (type == MFT_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
231
		MENUOUT("own");
232
	    else if (type == MFT_BITMAP)
Alexandre Julliard's avatar
Alexandre Julliard committed
233 234 235
		MENUOUT("bit");
	    else
		MENUOUT("???");
236
	    flags -= type;
Alexandre Julliard's avatar
Alexandre Julliard committed
237 238 239 240 241 242 243

	    MENUFLAG(MF_POPUP, "pop");
	    MENUFLAG(MFT_MENUBARBREAK, "barbrk");
	    MENUFLAG(MFT_MENUBREAK, "brk");
	    MENUFLAG(MFT_RADIOCHECK, "radio");
	    MENUFLAG(MFT_RIGHTORDER, "rorder");
	    MENUFLAG(MF_SYSMENU, "sys");
244
	    MENUFLAG(MFT_RIGHTJUSTIFY, "right");	/* same as MF_HELP */
Alexandre Julliard's avatar
Alexandre Julliard committed
245 246

	    if (flags)
247
		DPRINTF( "+0x%x", flags);
Alexandre Julliard's avatar
Alexandre Julliard committed
248 249 250 251
	}
	flags = mp->fState;
	if (flags) {
	    int count = 0;
252
	    DPRINTF( ", State=");
Alexandre Julliard's avatar
Alexandre Julliard committed
253
	    MENUFLAG(MFS_GRAYED, "grey");
254
	    MENUFLAG(MFS_DEFAULT, "default");
Alexandre Julliard's avatar
Alexandre Julliard committed
255 256 257 258 259 260
	    MENUFLAG(MFS_DISABLED, "dis");
	    MENUFLAG(MFS_CHECKED, "check");
	    MENUFLAG(MFS_HILITE, "hi");
	    MENUFLAG(MF_USECHECKBITMAPS, "usebit");
	    MENUFLAG(MF_MOUSESELECT, "mouse");
	    if (flags)
261
		DPRINTF( "+0x%x", flags);
Alexandre Julliard's avatar
Alexandre Julliard committed
262 263
	}
	if (mp->hCheckBit)
264
	    DPRINTF( ", Chk=%p", mp->hCheckBit);
Alexandre Julliard's avatar
Alexandre Julliard committed
265
	if (mp->hUnCheckBit)
266
	    DPRINTF( ", Unc=%p", mp->hUnCheckBit);
Alexandre Julliard's avatar
Alexandre Julliard committed
267

268
	if (type == MFT_STRING) {
Alexandre Julliard's avatar
Alexandre Julliard committed
269
	    if (mp->text)
270
		DPRINTF( ", Text=%s", debugstr_w(mp->text));
Alexandre Julliard's avatar
Alexandre Julliard committed
271
	    else
272
		DPRINTF( ", Text=Null");
Alexandre Julliard's avatar
Alexandre Julliard committed
273 274 275
	} else if (mp->text == NULL)
	    /* Nothing */ ;
	else
276
	    DPRINTF( ", Text=%p", mp->text);
277
	if (mp->dwItemData)
278 279
	    DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
	DPRINTF( " }");
Alexandre Julliard's avatar
Alexandre Julliard committed
280
    } else {
281
	DPRINTF( "NULL");
Alexandre Julliard's avatar
Alexandre Julliard committed
282
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
283

284
    DPRINTF(" %s\n", postfix);
Alexandre Julliard's avatar
Alexandre Julliard committed
285 286 287 288 289
}

#undef MENUOUT
#undef MENUFLAG

290 291 292 293 294 295

/***********************************************************************
 *           MENU_GetMenu
 *
 * Validate the given menu handle and returns the menu structure pointer.
 */
296
static POPUPMENU *MENU_GetMenu(HMENU hMenu)
297
{
298 299
    POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
    if (!menu || menu->wMagic != MENU_MAGIC)
300
    {
301
        WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 303 304 305 306
        menu = NULL;
    }
    return menu;
}

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

Alexandre Julliard's avatar
Alexandre Julliard committed
324 325
/***********************************************************************
 *           MENU_CopySysPopup
Alexandre Julliard's avatar
Alexandre Julliard committed
326
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
327
 * Return the default system menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
328
 */
329
static HMENU MENU_CopySysPopup(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
330
{
331
    static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
332
    HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
Alexandre Julliard's avatar
Alexandre Julliard committed
333

Alexandre Julliard's avatar
Alexandre Julliard committed
334
    if( hMenu ) {
335
        POPUPMENU* menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
336
        menu->wFlags |= MF_SYSMENU | MF_POPUP;
337
	SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
338
    }
339
    else
340
	ERR("Unable to load default system menu\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
341

342
    TRACE("returning %p.\n", hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
343 344

    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
345 346 347
}


Alexandre Julliard's avatar
Alexandre Julliard committed
348 349
/**********************************************************************
 *           MENU_GetSysMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
350
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
351
 * Create a copy of the system menu. System menu in Windows is
352
 * a special menu bar with the single entry - system menu popup.
353 354
 * This popup is presented to the outside world as a "system menu".
 * However, the real system menu handle is sometimes seen in the
355
 * WM_MENUSELECT parameters (and Word 6 likes it this way).
Alexandre Julliard's avatar
Alexandre Julliard committed
356
 */
357
HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
358
{
359
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
360

361
    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
362
    if ((hMenu = CreateMenu()))
Alexandre Julliard's avatar
Alexandre Julliard committed
363
    {
364
	POPUPMENU *menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
365
	menu->wFlags = MF_SYSMENU;
366
	menu->hWnd = WIN_GetFullHandle( hWnd );
367
	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
368

369
	if (hPopupMenu == (HMENU)(-1))
Alexandre Julliard's avatar
Alexandre Julliard committed
370
	    hPopupMenu = MENU_CopySysPopup();
371
	else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
Alexandre Julliard's avatar
Alexandre Julliard committed
372

Alexandre Julliard's avatar
Alexandre Julliard committed
373 374
	if (hPopupMenu)
	{
375
	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
376
                         (UINT_PTR)hPopupMenu, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
377

Alexandre Julliard's avatar
Alexandre Julliard committed
378 379
            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
            menu->items[0].fState = 0;
380
            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
Alexandre Julliard's avatar
Alexandre Julliard committed
381

382
	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
383 384
	    return hMenu;
	}
385
	DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
386
    }
387
    ERR("failed to load system menu!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
388
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
389 390 391
}


Alexandre Julliard's avatar
Alexandre Julliard committed
392
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
393
 *           MENU_Init
Alexandre Julliard's avatar
Alexandre Julliard committed
394
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
395
 * Menus initialisation.
Alexandre Julliard's avatar
Alexandre Julliard committed
396
 */
397
BOOL MENU_Init(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
398
{
399
    HBITMAP hBitmap;
400
    NONCLIENTMETRICSW ncm;
401

Alexandre Julliard's avatar
Alexandre Julliard committed
402 403 404 405 406
    static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
					    0x55, 0, 0xAA, 0,
					    0x55, 0, 0xAA, 0,
					    0x55, 0, 0xAA, 0 };

Alexandre Julliard's avatar
Alexandre Julliard committed
407
    /* Load menu bitmaps */
408
    hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
409
    /* Load system buttons bitmaps */
410
    hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
Alexandre Julliard's avatar
Alexandre Julliard committed
411

Alexandre Julliard's avatar
Alexandre Julliard committed
412
    if (hStdMnArrow)
413 414
    {
	BITMAP bm;
415
	GetObjectW( hStdMnArrow, sizeof(bm), &bm );
416 417
	arrow_bitmap_width = bm.bmWidth;
	arrow_bitmap_height = bm.bmHeight;
Alexandre Julliard's avatar
Alexandre Julliard committed
418
    } else
419
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
420

421
    if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
422
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
423

424
    if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
425
	return FALSE;
426

427 428 429 430
    DeleteObject( hBitmap );
    if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
	return FALSE;

431 432
    ncm.cbSize = sizeof(NONCLIENTMETRICSW);
    if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
433
	return FALSE;
434

435
    if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
436 437
	return FALSE;

438 439 440 441
    ncm.lfMenuFont.lfWeight += 300;
    if ( ncm.lfMenuFont.lfWeight > 1000)
	ncm.lfMenuFont.lfWeight = 1000;

442
    if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
443 444
	return FALSE;

445
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
446 447
}

Alexandre Julliard's avatar
Alexandre Julliard committed
448 449 450 451 452
/***********************************************************************
 *           MENU_InitSysMenuPopup
 *
 * Grey the appropriate items in System menu.
 */
453
static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
Alexandre Julliard's avatar
Alexandre Julliard committed
454
{
455
    BOOL gray;
Alexandre Julliard's avatar
Alexandre Julliard committed
456 457

    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
458
    EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
459
    gray = ((style & WS_MAXIMIZE) != 0);
460
    EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
461
    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
462
    EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
463
    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
464
    EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
465
    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
466
    EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
467
    gray = (clsStyle & CS_NOCLOSE) != 0;
468 469 470 471

    /* 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
472 473 474
}


Alexandre Julliard's avatar
Alexandre Julliard committed
475 476
/******************************************************************************
 *
477 478
 *   UINT  MENU_GetStartOfNextColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
479 480 481
 *
 *****************************************************************************/

482 483
static UINT  MENU_GetStartOfNextColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
484
{
485 486
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
487 488 489 490

    if(!menu)
	return NO_SELECTED_ITEM;

491
    i = menu->FocusedItem + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
492 493 494 495
    if( i == NO_SELECTED_ITEM )
	return i;

    for( ; i < menu->nItems; ++i ) {
Alexandre Julliard's avatar
Alexandre Julliard committed
496
	if (menu->items[i].fType & MF_MENUBARBREAK)
Alexandre Julliard's avatar
Alexandre Julliard committed
497 498 499 500 501 502 503 504 505
	    return i;
    }

    return NO_SELECTED_ITEM;
}


/******************************************************************************
 *
506 507
 *   UINT  MENU_GetStartOfPrevColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
508 509 510
 *
 *****************************************************************************/

511 512
static UINT  MENU_GetStartOfPrevColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
513
{
514
    POPUPMENU *menu = MENU_GetMenu(hMenu);
515
    UINT  i;
Alexandre Julliard's avatar
Alexandre Julliard committed
516 517 518 519 520 521 522 523 524 525

    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 &&
Alexandre Julliard's avatar
Alexandre Julliard committed
526
	 !(menu->items[i].fType & MF_MENUBARBREAK);
Alexandre Julliard's avatar
Alexandre Julliard committed
527 528 529 530 531 532
	--i); /* empty */

    if(i == 0)
	return NO_SELECTED_ITEM;

    for(--i; i != 0; --i) {
Alexandre Julliard's avatar
Alexandre Julliard committed
533
	if (menu->items[i].fType & MF_MENUBARBREAK)
Alexandre Julliard's avatar
Alexandre Julliard committed
534 535 536
	    break;
    }

537
    TRACE("ret %d.\n", i );
Alexandre Julliard's avatar
Alexandre Julliard committed
538 539 540 541 542 543

    return i;
}



Alexandre Julliard's avatar
Alexandre Julliard committed
544 545 546 547 548 549
/***********************************************************************
 *           MENU_FindItem
 *
 * Find a menu item. Return a pointer on the item, and modifies *hmenu
 * in case the item was in a sub-menu.
 */
550
static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
551 552
{
    POPUPMENU *menu;
553
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
554

555
    if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
556 557 558
    if (wFlags & MF_BYPOSITION)
    {
	if (*nPos >= menu->nItems) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
559
	return &menu->items[*nPos];
Alexandre Julliard's avatar
Alexandre Julliard committed
560 561 562
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
563
        MENUITEM *item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
564 565
	for (i = 0; i < menu->nItems; i++, item++)
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
566
	    if (item->wID == *nPos)
Alexandre Julliard's avatar
Alexandre Julliard committed
567 568 569 570
	    {
		*nPos = i;
		return item;
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
571
	    else if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
572
	    {
573
		HMENU hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
574 575 576 577 578 579 580 581 582 583 584 585
		MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
		if (subitem)
		{
		    *hmenu = hsubmenu;
		    return subitem;
		}
	    }
	}
    }
    return NULL;
}

586 587 588
/***********************************************************************
 *           MENU_FindSubMenu
 *
589
 * Find a Sub menu. Return the position of the submenu, and modifies
590 591 592 593 594 595 596 597
 * *hmenu in case it is found in another sub-menu.
 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
 */
UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
{
    POPUPMENU *menu;
    UINT i;
    MENUITEM *item;
598
    if (((*hmenu)==(HMENU)0xffff) ||
599
            (!(menu = MENU_GetMenu(*hmenu))))
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
        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
619 620 621 622 623 624
/***********************************************************************
 *           MENU_FreeItemData
 */
static void MENU_FreeItemData( MENUITEM* item )
{
    /* delete text */
Alexandre Julliard's avatar
Alexandre Julliard committed
625
    if (IS_STRING_ITEM(item->fType) && item->text)
626
        HeapFree( GetProcessHeap(), 0, item->text );
Alexandre Julliard's avatar
Alexandre Julliard committed
627
}
Alexandre Julliard's avatar
Alexandre Julliard committed
628 629 630 631

/***********************************************************************
 *           MENU_FindItemByCoords
 *
632 633
 * 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
634
 * an arbitrary system menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
635
 */
636
static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
637
					POINT pt, UINT *pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
638 639
{
    MENUITEM *item;
640
    UINT i;
641
    RECT wrect;
Alexandre Julliard's avatar
Alexandre Julliard committed
642

643 644
    if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
    pt.x -= wrect.left;pt.y -= wrect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
645
    item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
646 647
    for (i = 0; i < menu->nItems; i++, item++)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
648 649
	if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
	    (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
Alexandre Julliard's avatar
Alexandre Julliard committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
	{
	    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.
 */
665
static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
666
                                WCHAR key, BOOL forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
667
{
668
    TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
669

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

Alexandre Julliard's avatar
Alexandre Julliard committed
672
    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
673
    {
674
	POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
675
	MENUITEM *item = menu->items;
676
	LRESULT menuchar;
Alexandre Julliard's avatar
Alexandre Julliard committed
677 678

	if( !forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
679
	{
680
	     UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
681

682
	     key = toupperW(key);
Alexandre Julliard's avatar
Alexandre Julliard committed
683 684
	     for (i = 0; i < menu->nItems; i++, item++)
	     {
685
		if (IS_STRING_ITEM(item->fType) && item->text)
Alexandre Julliard's avatar
Alexandre Julliard committed
686
		{
687
		    WCHAR *p = item->text - 2;
688 689
		    do
		    {
690
		    	p = strchrW (p + 2, '&');
691 692
		    }
		    while (p != NULL && p [1] == '&');
693
		    if (p && (toupperW(p[1]) == key)) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
694 695
		}
	     }
Alexandre Julliard's avatar
Alexandre Julliard committed
696
	}
697
	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
698
                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
699
	if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
700
	if (HIWORD(menuchar) == 1) return (UINT)(-2);
Alexandre Julliard's avatar
Alexandre Julliard committed
701
    }
702
    return (UINT)(-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
703
}
704 705


706
/***********************************************************************
707
 *           MENU_GetBitmapItemSize
708
 *
709
 * Get the size of a bitmap item.
710
 */
711 712 713 714 715 716
static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
{
    BITMAP bm;
    HBITMAP bmp = (HBITMAP)id;

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

718 719 720 721 722
    /* check if there is a magic menu item associated with this item */
    if (id && IS_MAGIC_ITEM( id ))
    {
        switch(LOWORD(id))
        {
723
        case (INT_PTR)HBMMENU_SYSTEM:
724 725 726 727 728 729
            if (data)
            {
                bmp = (HBITMAP)data;
                break;
            }
            /* fall through */
730 731 732 733 734
        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:
735 736
            size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
            size->cy = size->cx;
737
            return;
738 739 740 741 742
        case (INT_PTR)HBMMENU_CALLBACK:
        case (INT_PTR)HBMMENU_POPUP_CLOSE:
        case (INT_PTR)HBMMENU_POPUP_RESTORE:
        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
743 744 745 746 747
        default:
            FIXME("Magic 0x%08x not implemented\n", id);
            return;
        }
    }
748
    if (GetObjectW(bmp, sizeof(bm), &bm ))
749 750 751 752 753 754 755 756 757 758 759 760
    {
        size->cx = bm.bmWidth;
        size->cy = bm.bmHeight;
    }
}

/***********************************************************************
 *           MENU_DrawBitmapItem
 *
 * Draw a bitmap item.
 */
static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
761
{
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    BITMAP bm;
    DWORD rop;
    HDC hdcMem;
    HBITMAP bmp = (HBITMAP)lpitem->text;
    int w = rect->right - rect->left;
    int h = rect->bottom - rect->top;
    int bmp_xoffset = 0;
    int left, top;

    /* Check if there is a magic menu item associated with this item */
    if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
    {
        UINT flags = 0;
        RECT r;

        switch(LOWORD(lpitem->text))
        {
779
        case (INT_PTR)HBMMENU_SYSTEM:
780 781 782
            if (lpitem->dwItemData)
            {
                bmp = (HBITMAP)lpitem->dwItemData;
783
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
784 785 786 787
            }
            else
            {
                bmp = hBmpSysMenu;
788
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
789 790 791 792 793
                /* only use right half of the bitmap */
                bmp_xoffset = bm.bmWidth / 2;
                bm.bmWidth -= bmp_xoffset;
            }
            goto got_bitmap;
794
        case (INT_PTR)HBMMENU_MBAR_RESTORE:
795 796
            flags = DFCS_CAPTIONRESTORE;
            break;
797
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
798 799
            flags = DFCS_CAPTIONMIN;
            break;
800
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
801 802
            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
            break;
803
        case (INT_PTR)HBMMENU_MBAR_CLOSE:
804 805
            flags = DFCS_CAPTIONCLOSE;
            break;
806
        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
807 808
            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
            break;
809 810 811 812 813
        case (INT_PTR)HBMMENU_CALLBACK:
        case (INT_PTR)HBMMENU_POPUP_CLOSE:
        case (INT_PTR)HBMMENU_POPUP_RESTORE:
        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
814 815 816 817 818 819 820 821 822
        default:
            FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
            return;
        }
        r = *rect;
        InflateRect( &r, -1, -1 );
        if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
        DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
        return;
823 824
    }

825
    if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
826 827 828 829 830 831 832 833

 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;
834 835 836
    rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
    if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
        SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
837 838
    BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
    DeleteDC( hdcMem );
839
}
Alexandre Julliard's avatar
Alexandre Julliard committed
840

841

Alexandre Julliard's avatar
Alexandre Julliard committed
842 843 844 845 846
/***********************************************************************
 *           MENU_CalcItemSize
 *
 * Calculate the size of the menu item and store it in lpitem->rect.
 */
847 848
static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
			       INT orgX, INT orgY, BOOL menuBar )
Alexandre Julliard's avatar
Alexandre Julliard committed
849
{
850
    WCHAR *p;
851
    UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
Alexandre Julliard's avatar
Alexandre Julliard committed
852

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
859
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
860
    {
861 862 863 864 865 866 867
        /*
        ** Experimentation under Windows reveals that an owner-drawn
        ** menu is expected to return the size of the content part of
        ** the menu item, not including the checkmark nor the submenu
        ** arrow.  Windows adds those values itself and returns the
        ** enlarged rectangle on subsequent WM_DRAWITEM messages.
        */
868
        MEASUREITEMSTRUCT mis;
Alexandre Julliard's avatar
Alexandre Julliard committed
869
        mis.CtlType    = ODT_MENU;
870
        mis.CtlID      = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
871
        mis.itemID     = lpitem->wID;
872 873 874
        mis.itemData   = (DWORD)lpitem->dwItemData;
        mis.itemHeight = 0;
        mis.itemWidth  = 0;
875
        SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
Alexandre Julliard's avatar
Alexandre Julliard committed
876
        lpitem->rect.right  += mis.itemWidth;
877 878 879 880

 	if (menuBar)
	{
	     lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
881

882 883 884

             /* under at least win95 you seem to be given a standard
                height for the menu and the height value is ignored */
885
	     lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
886 887 888
        }
        else
            lpitem->rect.bottom += mis.itemHeight;
889

890
	TRACE("id=%04x size=%dx%d\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
891
                     lpitem->wID, mis.itemWidth, mis.itemHeight);
892
        /* Fall through to get check/arrow width calculation. */
893
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
894

Alexandre Julliard's avatar
Alexandre Julliard committed
895
    if (lpitem->fType & MF_SEPARATOR)
Alexandre Julliard's avatar
Alexandre Julliard committed
896 897 898 899 900
    {
	lpitem->rect.bottom += SEPARATOR_HEIGHT;
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
901 902 903
    if (!menuBar)
    {
	lpitem->rect.right += 2 * check_bitmap_width;
Alexandre Julliard's avatar
Alexandre Julliard committed
904
	if (lpitem->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
905 906
	    lpitem->rect.right += arrow_bitmap_width;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
907

908 909 910
    if (lpitem->fType & MF_OWNERDRAW)
        return;

911
    if (IS_BITMAP_ITEM(lpitem->fType))
Alexandre Julliard's avatar
Alexandre Julliard committed
912
    {
913
        SIZE size;
914

915 916 917
        MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
        lpitem->rect.right  += size.cx;
        lpitem->rect.bottom += size.cy;
918 919 920
        /* Leave space for the sunken border */
        lpitem->rect.right  += 2;
        lpitem->rect.bottom += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
921
    }
922

Alexandre Julliard's avatar
Alexandre Julliard committed
923

924 925
    /* it must be a text item - unless it's the system menu */
    if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
926 927
    {   SIZE size;

928
	GetTextExtentPoint32W(hdc, lpitem->text,  strlenW(lpitem->text), &size);
929

930
	lpitem->rect.right  += size.cx;
931
	lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
932
	lpitem->xTab = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
933

934 935 936 937
	if (menuBar)
	{
	     lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
	}
938
	else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
939 940
	{
	    /* Item contains a tab (only meaningful in popup menus) */
941
	    GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
942 943 944 945 946
	    lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
	    lpitem->rect.right += MENU_TAB_SPACE;
	}
	else
	{
947
	    if (strchrW( lpitem->text, '\b' ))
948
	        lpitem->rect.right += MENU_TAB_SPACE;
949
	    lpitem->xTab = lpitem->rect.right - check_bitmap_width
950 951
	                   - arrow_bitmap_width;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
952
    }
953
    TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
954 955 956 957 958 959 960 961
}


/***********************************************************************
 *           MENU_PopupMenuCalcSize
 *
 * Calculate the size of a popup menu.
 */
962
static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
Alexandre Julliard's avatar
Alexandre Julliard committed
963
{
Alexandre Julliard's avatar
Alexandre Julliard committed
964
    MENUITEM *lpitem;
965
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
966 967
    int start, i;
    int orgX, orgY, maxX, maxTab, maxTabWidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
968 969 970

    lppop->Width = lppop->Height = 0;
    if (lppop->nItems == 0) return;
971
    hdc = GetDC( 0 );
972 973

    SelectObject( hdc, hMenuFont);
974

Alexandre Julliard's avatar
Alexandre Julliard committed
975
    start = 0;
976
    maxX = 2 + 1;
977

Alexandre Julliard's avatar
Alexandre Julliard committed
978
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
979
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
980
	lpitem = &lppop->items[start];
Alexandre Julliard's avatar
Alexandre Julliard committed
981
	orgX = maxX;
982
	orgY = 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
983

Alexandre Julliard's avatar
Alexandre Julliard committed
984
	maxTab = maxTabWidth = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
985 986

	  /* Parse items until column break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
987
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
988
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
989
	    if ((i != start) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
990
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
991

Alexandre Julliard's avatar
Alexandre Julliard committed
992
	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
993

994
	    if (lpitem->fType & MF_MENUBARBREAK) orgX++;
995
	    maxX = max( maxX, lpitem->rect.right );
Alexandre Julliard's avatar
Alexandre Julliard committed
996
	    orgY = lpitem->rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
997
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
Alexandre Julliard's avatar
Alexandre Julliard committed
998
	    {
999 1000
		maxTab = max( maxTab, lpitem->xTab );
		maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
Alexandre Julliard's avatar
Alexandre Julliard committed
1001
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1002 1003 1004
	}

	  /* Finish the column (set all items to the largest width found) */
1005
	maxX = max( maxX, maxTab + maxTabWidth );
Alexandre Julliard's avatar
Alexandre Julliard committed
1006
	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1007 1008
	{
	    lpitem->rect.right = maxX;
Alexandre Julliard's avatar
Alexandre Julliard committed
1009
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1010
		lpitem->xTab = maxTab;
1011

Alexandre Julliard's avatar
Alexandre Julliard committed
1012
	}
1013
	lppop->Height = max( lppop->Height, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1014 1015
    }

1016 1017 1018
    lppop->Width  = maxX;

    /* space for 3d border */
1019 1020
    lppop->Height += 2;
    lppop->Width += 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
1021

1022
    ReleaseDC( 0, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1023 1024 1025 1026 1027 1028
}


/***********************************************************************
 *           MENU_MenuBarCalcSize
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1029
 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
Alexandre Julliard's avatar
Alexandre Julliard committed
1030 1031 1032
 * 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
1033 1034
 * Calculate the size of the menu bar.
 */
1035 1036
static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
                                  LPPOPUPMENU lppop, HWND hwndOwner )
Alexandre Julliard's avatar
Alexandre Julliard committed
1037
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1038
    MENUITEM *lpitem;
Alexandre Julliard's avatar
Alexandre Julliard committed
1039
    int start, i, orgX, orgY, maxY, helpPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1040 1041 1042

    if ((lprect == NULL) || (lppop == NULL)) return;
    if (lppop->nItems == 0) return;
1043 1044
    TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
          lprect->left, lprect->top, lprect->right, lprect->bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
1045 1046
    lppop->Width  = lprect->right - lprect->left;
    lppop->Height = 0;
1047
    maxY = lprect->top+1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1048
    start = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1049
    helpPos = -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1050
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1051
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1052
	lpitem = &lppop->items[start];
Alexandre Julliard's avatar
Alexandre Julliard committed
1053 1054 1055 1056
	orgX = lprect->left;
	orgY = maxY;

	  /* Parse items until line break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
1057
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1058
	{
1059
	    if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1060
	    if ((i != start) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1061
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1062

1063
	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1064
			 orgX, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1065
	    debug_print_menuitem ("  item: ", lpitem, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
1066
	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1067

Alexandre Julliard's avatar
Alexandre Julliard committed
1068 1069
	    if (lpitem->rect.right > lprect->right)
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1070
		if (i != start) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1071 1072
		else lpitem->rect.right = lprect->right;
	    }
1073
	    maxY = max( maxY, lpitem->rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
1074 1075 1076 1077
	    orgX = lpitem->rect.right;
	}

	  /* Finish the line (set all items to the largest height found) */
Alexandre Julliard's avatar
Alexandre Julliard committed
1078
	while (start < i) lppop->items[start++].rect.bottom = maxY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1079 1080 1081 1082
    }

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

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096
    /* Flush right all items between the MF_RIGHTJUSTIFY and */
    /* the last item (if several lines, only move the last line) */
    lpitem = &lppop->items[lppop->nItems-1];
    orgY = lpitem->rect.top;
    orgX = lprect->right;
    for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
        if ( (helpPos==-1) || (helpPos>i) )
            break;				/* done */
        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
1097
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1098 1099 1100 1101 1102 1103 1104
}

/***********************************************************************
 *           MENU_DrawMenuItem
 *
 * Draw a single menu item.
 */
1105
static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1106
			       UINT height, BOOL menuBar, UINT odaction )
Alexandre Julliard's avatar
Alexandre Julliard committed
1107
{
1108
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1109

Alexandre Julliard's avatar
Alexandre Julliard committed
1110
    debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
1111 1112

    if (lpitem->fType & MF_SYSMENU)
Alexandre Julliard's avatar
Alexandre Julliard committed
1113
    {
1114 1115
	if( !IsIconic(hwnd) )
	    NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1116 1117 1118
	return;
    }

1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
      /* Setup colors */

    if (lpitem->fState & MF_HILITE)
    {
        if(menuBar) {
	    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 ) );
	SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1143
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1144
    {
1145 1146 1147 1148 1149 1150 1151 1152 1153
        /*
        ** 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.
        */
1154
        DRAWITEMSTRUCT dis;
Alexandre Julliard's avatar
Alexandre Julliard committed
1155

Alexandre Julliard's avatar
Alexandre Julliard committed
1156
        dis.CtlType   = ODT_MENU;
1157
	dis.CtlID     = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1158
        dis.itemID    = lpitem->wID;
1159
        dis.itemData  = (DWORD)lpitem->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
1160
        dis.itemState = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1161
        if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1162
        if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1163
        if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1164
        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1165
        dis.hwndItem   = (HWND)hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1166
        dis.hDC        = hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1167
        dis.rcItem     = lpitem->rect;
1168
        TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1169
	      "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1170
	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1171 1172
	      dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
	      dis.rcItem.bottom);
1173
        SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1174
        /* Fall through to draw popup-menu arrow */
Alexandre Julliard's avatar
Alexandre Julliard committed
1175
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1176

1177
    TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1178
					lpitem->rect.right,lpitem->rect.bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
1179

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

1182
    rect = lpitem->rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1183

1184 1185
    if (!(lpitem->fType & MF_OWNERDRAW))
    {
1186 1187
	if (lpitem->fState & MF_HILITE)
	{
1188 1189 1190 1191
	     if(menuBar)
		DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
	     else
		FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1192
	}
1193 1194 1195
        else
	    FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1196

1197
    SetBkMode( hdc, TRANSPARENT );
Alexandre Julliard's avatar
Alexandre Julliard committed
1198

1199
    if (!(lpitem->fType & MF_OWNERDRAW))
Alexandre Julliard's avatar
Alexandre Julliard committed
1200
    {
1201 1202 1203
        /* vertical separator */
        if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
        {
1204 1205 1206 1207
	    RECT rc = rect;
	    rc.top = 3;
	    rc.bottom = height - 3;
	    DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1208
        }
1209

1210 1211 1212
        /* horizontal separator */
        if (lpitem->fType & MF_SEPARATOR)
        {
1213 1214 1215 1216 1217
	    RECT rc = rect;
	    rc.left++;
	    rc.right--;
	    rc.top += SEPARATOR_HEIGHT / 2;
	    DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1218 1219
	    return;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1220 1221
    }

1222 1223
	/* helper lines for debugging */
/*	FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1224
	SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1225
	MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1226 1227 1228
	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
*/

Alexandre Julliard's avatar
Alexandre Julliard committed
1229 1230
    if (!menuBar)
    {
1231
	INT	y = rect.top + rect.bottom;
1232 1233
        UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
        UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
Alexandre Julliard's avatar
Alexandre Julliard committed
1234

1235 1236 1237 1238 1239
        if (!(lpitem->fType & MF_OWNERDRAW))
        {
	      /* Draw the check mark
	       *
	       * FIXME:
1240
	       * Custom checkmark bitmaps are monochrome but not always 1bpp.
1241
	       */
1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
            HBITMAP 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, rect.left, (y - check_bitmap_height) / 2,
                        check_bitmap_width, check_bitmap_height,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
            }
            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, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                DeleteObject( bm );
            }
1267
        }
1268

Alexandre Julliard's avatar
Alexandre Julliard committed
1269
	  /* Draw the popup-menu arrow */
Alexandre Julliard's avatar
Alexandre Julliard committed
1270
	if (lpitem->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
1271
	{
1272
	    HDC hdcMem = CreateCompatibleDC( hdc );
1273
	    HBITMAP hOrigBitmap;
1274

1275
	    hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1276
	    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1277 1278 1279
		      (y - arrow_bitmap_height) / 2,
		      arrow_bitmap_width, arrow_bitmap_height,
		      hdcMem, 0, 0, SRCCOPY );
1280
            SelectObject( hdcMem, hOrigBitmap );
1281
	    DeleteDC( hdcMem );
Alexandre Julliard's avatar
Alexandre Julliard committed
1282 1283 1284 1285 1286 1287
	}

	rect.left += check_bitmap_width;
	rect.right -= arrow_bitmap_width;
    }

1288 1289 1290 1291
    /* Done for owner-drawn */
    if (lpitem->fType & MF_OWNERDRAW)
        return;

1292 1293
    /* Draw the item text or bitmap */
    if (IS_BITMAP_ITEM(lpitem->fType))
1294
    {
1295
        MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
Alexandre Julliard's avatar
Alexandre Julliard committed
1296
	return;
1297

Alexandre Julliard's avatar
Alexandre Julliard committed
1298
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1299
    /* No bitmap - process text if present */
Alexandre Julliard's avatar
Alexandre Julliard committed
1300
    else if (IS_STRING_ITEM(lpitem->fType))
Alexandre Julliard's avatar
Alexandre Julliard committed
1301
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1302
	register int i;
1303
	HFONT hfontOld = 0;
1304

1305 1306 1307
	UINT uFormat = (menuBar) ?
			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
			DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1308

1309 1310 1311 1312 1313
	if ( lpitem->fState & MFS_DEFAULT )
	{
	     hfontOld = SelectObject( hdc, hMenuFontBold);
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
1314 1315 1316 1317
	if (menuBar)
	{
	    rect.left += MENU_BAR_ITEMS_SPACE / 2;
	    rect.right -= MENU_BAR_ITEMS_SPACE / 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
1318 1319
	}

1320 1321 1322 1323
	for (i = 0; lpitem->text[i]; i++)
	    if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
	        break;

1324
	if(lpitem->fState & MF_GRAYED)
1325
	{
1326
	    if (!(lpitem->fState & MF_HILITE) )
1327
	    {
1328
		++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1329
		SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1330
		DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1331
		--rect.left; --rect.top; --rect.right; --rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1332
	    }
1333
	    SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
Alexandre Julliard's avatar
Alexandre Julliard committed
1334
	}
1335

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

1338
	/* paint the shortcut text */
1339
	if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
Alexandre Julliard's avatar
Alexandre Julliard committed
1340
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
1341
	    if (lpitem->text[i] == '\t')
Alexandre Julliard's avatar
Alexandre Julliard committed
1342 1343
	    {
		rect.left = lpitem->xTab;
1344
		uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1345
	    }
1346
	    else
1347 1348 1349 1350
	    {
		uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
	    }

1351
	    if(lpitem->fState & MF_GRAYED)
1352 1353 1354 1355 1356
	    {
		if (!(lpitem->fState & MF_HILITE) )
		{
		    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
		    SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1357
		    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1358 1359 1360 1361
		    --rect.left; --rect.top; --rect.right; --rect.bottom;
		}
		SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
	    }
1362
	    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1363
	}
1364

1365
	if (hfontOld)
1366
	    SelectObject (hdc, hfontOld);
Alexandre Julliard's avatar
Alexandre Julliard committed
1367 1368 1369 1370
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1371
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1372 1373 1374
 *           MENU_DrawPopupMenu
 *
 * Paint a popup menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
1375
 */
1376
static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1377
{
1378 1379
    HBRUSH hPrevBrush = 0;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1380

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

1383
    GetClientRect( hwnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1384

1385
    if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1386
        && (SelectObject( hdc, hMenuFont)))
Alexandre Julliard's avatar
Alexandre Julliard committed
1387
    {
1388
	HPEN hPrevPen;
1389

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

1392
	hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1393 1394 1395 1396
	if( hPrevPen )
	{
	    POPUPMENU *menu;

1397
	    DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1398 1399 1400

	    /* draw menu items */

1401
	    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1402 1403 1404
	    if (menu && menu->nItems)
	    {
		MENUITEM *item;
1405
		UINT u;
Alexandre Julliard's avatar
Alexandre Julliard committed
1406 1407

		for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1408
		    MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1409
				       menu->Height, FALSE, ODA_DRAWENTIRE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1410 1411

	    }
1412
	} else
1413 1414 1415
	{
	    SelectObject( hdc, hPrevBrush );
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1416
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1417
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1418

Alexandre Julliard's avatar
Alexandre Julliard committed
1419 1420 1421 1422
/***********************************************************************
 *           MENU_DrawMenuBar
 *
 * Paint a menu bar. Returns the height of the menu bar.
1423
 * called from [windows/nonclient.c]
Alexandre Julliard's avatar
Alexandre Julliard committed
1424
 */
1425 1426
UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
                         BOOL suppress_draw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1427 1428
{
    LPPOPUPMENU lppop;
1429
    HFONT hfontOld = 0;
1430
    HMENU hMenu = GetMenu(hwnd);
1431

1432
    lppop = MENU_GetMenu( hMenu );
1433 1434
    if (lppop == NULL || lprect == NULL)
    {
1435
        return GetSystemMetrics(SM_CYMENU);
1436
    }
1437

1438 1439
    if (suppress_draw)
    {
1440
	hfontOld = SelectObject( hDC, hMenuFont);
1441

1442 1443
	if (lppop->Height == 0)
		MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1444

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

1447 1448
        if (hfontOld) SelectObject( hDC, hfontOld);
	return lppop->Height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1449
    }
1450 1451
    else
        return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1452
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1453

1454

Alexandre Julliard's avatar
Alexandre Julliard committed
1455 1456 1457 1458 1459
/***********************************************************************
 *           MENU_ShowPopup
 *
 * Display a popup menu.
 */
1460 1461
static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
                              INT x, INT y, INT xanchor, INT yanchor )
Alexandre Julliard's avatar
Alexandre Julliard committed
1462
{
1463 1464
    POPUPMENU *menu;
    UINT width, height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1465

1466 1467
    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);
1468

1469
    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1470 1471
    if (menu->FocusedItem != NO_SELECTED_ITEM)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1472
	menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1473 1474
	menu->FocusedItem = NO_SELECTED_ITEM;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1475

1476
    /* store the owner for DrawItem */
1477 1478
    menu->hwndOwner = hwndOwner;

Alexandre Julliard's avatar
Alexandre Julliard committed
1479

1480
    MENU_PopupMenuCalcSize( menu, hwndOwner );
Alexandre Julliard's avatar
Alexandre Julliard committed
1481

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

1484
    width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1485
    height = menu->Height + GetSystemMetrics(SM_CYBORDER);
Alexandre Julliard's avatar
Alexandre Julliard committed
1486

1487 1488 1489 1490 1491 1492 1493 1494
    if( x + width > GetSystemMetrics(SM_CXSCREEN ))
    {
        if( xanchor )
            x -= width - xanchor;
        if( x + width > GetSystemMetrics(SM_CXSCREEN))
            x = GetSystemMetrics(SM_CXSCREEN) - width;
    }
    if( x < 0 ) x = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1495

1496 1497 1498 1499 1500 1501 1502 1503
    if( y + height > GetSystemMetrics(SM_CYSCREEN ))
    {
        if( yanchor )
            y -= height + yanchor;
        if( y + height > GetSystemMetrics(SM_CYSCREEN ))
            y = GetSystemMetrics(SM_CYSCREEN) - height;
    }
    if( y < 0 ) y = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1504

1505
    /* NOTE: In Windows, top menu popup is not owned. */
1506
    menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1507
                                WS_POPUP, x, y, width, height,
1508
                                hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1509 1510 1511
                                (LPVOID)hmenu );
    if( !menu->hWnd ) return FALSE;
    if (!top_popup) top_popup = menu->hWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
1512

1513 1514 1515 1516 1517 1518
    /* Display the window */

    SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
                  SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
    UpdateWindow( menu->hWnd );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1519 1520 1521
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1522 1523 1524
/***********************************************************************
 *           MENU_SelectItem
 */
1525
static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1526
                             BOOL sendMenuSelect, HMENU topmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1527
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1528
    LPPOPUPMENU lppop;
1529
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1530

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

1533
    lppop = MENU_GetMenu( hmenu );
1534
    if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1535

Alexandre Julliard's avatar
Alexandre Julliard committed
1536
    if (lppop->FocusedItem == wIndex) return;
1537 1538
    if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
    else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1539
    if (!top_popup) top_popup = lppop->hWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
1540

1541 1542
    SelectObject( hdc, hMenuFont);

Alexandre Julliard's avatar
Alexandre Julliard committed
1543
      /* Clear previous highlighted item */
1544
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
1545
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1546
	lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1547
	MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
Alexandre Julliard's avatar
Alexandre Julliard committed
1548 1549
                          lppop->Height, !(lppop->wFlags & MF_POPUP),
			  ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
1550 1551 1552 1553
    }

      /* Highlight new item (if any) */
    lppop->FocusedItem = wIndex;
1554
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
1555
    {
1556 1557
        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
            lppop->items[wIndex].fState |= MF_HILITE;
1558
            MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1559 1560 1561
                    &lppop->items[wIndex], lppop->Height,
                    !(lppop->wFlags & MF_POPUP), ODA_SELECT );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1562
        if (sendMenuSelect)
Alexandre Julliard's avatar
Alexandre Julliard committed
1563 1564
        {
            MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1565
	    SendMessageW( hwndOwner, WM_MENUSELECT,
1566 1567
                     MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
                     ip->fType | ip->fState | MF_MOUSESELECT |
1568
                     (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
1569
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1570
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1571
    else if (sendMenuSelect) {
1572 1573 1574
        if(topmenu){
            int pos;
            if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1575
                POPUPMENU *ptm = MENU_GetMenu( topmenu );
1576
                MENUITEM *ip = &ptm->items[pos];
1577
                SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1578
                         ip->fType | ip->fState | MF_MOUSESELECT |
1579
                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1580 1581
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1582
    }
1583
    ReleaseDC( lppop->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1584 1585 1586 1587
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1588
 *           MENU_MoveSelection
Alexandre Julliard's avatar
Alexandre Julliard committed
1589
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1590 1591 1592
 * 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
1593
 */
1594
static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
Alexandre Julliard's avatar
Alexandre Julliard committed
1595
{
1596
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1597 1598
    POPUPMENU *menu;

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

1601 1602
    menu = MENU_GetMenu( hmenu );
    if ((!menu) || (!menu->items)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1603 1604

    if ( menu->FocusedItem != NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
1605
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1606
	if( menu->nItems == 1 ) return; else
1607
	for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
Alexandre Julliard's avatar
Alexandre Julliard committed
1608
					    ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
1609
	    if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
1610
	    {
1611
		MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1612
		return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1613
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1614
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1615

1616
    for ( i = (offset > 0) ? 0 : menu->nItems - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1617
		  i >= 0 && i < menu->nItems ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
1618
	if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
1619
	{
1620
	    MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1621 1622
	    return;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1623 1624 1625
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1626 1627 1628
/**********************************************************************
 *         MENU_SetItemData
 *
1629
 * Set an item's flags, id and text ptr. Called by InsertMenu() and
Alexandre Julliard's avatar
Alexandre Julliard committed
1630
 * ModifyMenu().
Alexandre Julliard's avatar
Alexandre Julliard committed
1631
 */
1632
static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1633
                                LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
1634
{
1635
    LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1636

Alexandre Julliard's avatar
Alexandre Julliard committed
1637
    debug_print_menuitem("MENU_SetItemData from: ", item, "");
1638
    TRACE("flags=%x str=%p\n", flags, str);
Alexandre Julliard's avatar
Alexandre Julliard committed
1639

Alexandre Julliard's avatar
Alexandre Julliard committed
1640 1641
    if (IS_STRING_ITEM(flags))
    {
1642
        if (!str)
Alexandre Julliard's avatar
Alexandre Julliard committed
1643 1644
        {
            flags |= MF_SEPARATOR;
Alexandre Julliard's avatar
Alexandre Julliard committed
1645
            item->text = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1646 1647 1648
        }
        else
        {
1649
            LPWSTR text;
Alexandre Julliard's avatar
Alexandre Julliard committed
1650 1651 1652 1653 1654 1655
            /* Item beginning with a backspace is a help item */
            if (*str == '\b')
            {
                flags |= MF_HELP;
                str++;
            }
1656 1657 1658
            if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
                return FALSE;
            strcpyW( text, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
1659
            item->text = text;
Alexandre Julliard's avatar
Alexandre Julliard committed
1660
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1661
    }
1662
    else if (IS_BITMAP_ITEM(flags))
1663
        item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
Alexandre Julliard's avatar
Alexandre Julliard committed
1664
    else item->text = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1665

1666
    if (flags & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1667 1668 1669 1670
        item->dwItemData = (DWORD)str;
    else
        item->dwItemData = 0;

1671
    if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1672
	DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */
Alexandre Julliard's avatar
Alexandre Julliard committed
1673 1674 1675

    if (flags & MF_POPUP)
    {
1676
	POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1677
        if (menu) menu->wFlags |= MF_POPUP;
Alexandre Julliard's avatar
Alexandre Julliard committed
1678
	else
Alexandre Julliard's avatar
Alexandre Julliard committed
1679 1680 1681 1682 1683 1684 1685
        {
            item->wID = 0;
            item->hSubMenu = 0;
            item->fType = 0;
            item->fState = 0;
	    return FALSE;
        }
1686
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1687 1688

    item->wID = id;
1689
    if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
Alexandre Julliard's avatar
Alexandre Julliard committed
1690 1691 1692

    if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
      flags |= MF_POPUP; /* keep popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
1693

Alexandre Julliard's avatar
Alexandre Julliard committed
1694 1695 1696
    item->fType = flags & TYPE_MASK;
    item->fState = (flags & STATE_MASK) &
        ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
Alexandre Julliard's avatar
Alexandre Julliard committed
1697

Alexandre Julliard's avatar
Alexandre Julliard committed
1698 1699 1700 1701

    /* Don't call SetRectEmpty here! */


1702
    if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
Alexandre Julliard's avatar
Alexandre Julliard committed
1703

Alexandre Julliard's avatar
Alexandre Julliard committed
1704
    debug_print_menuitem("MENU_SetItemData to  : ", item, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
1705 1706 1707 1708 1709 1710 1711
    return TRUE;
}


/**********************************************************************
 *         MENU_InsertItem
 *
1712
 * Insert (allocate) a new item into a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
1713
 */
1714
static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
1715 1716 1717 1718
{
    MENUITEM *newItems;
    POPUPMENU *menu;

1719
    if (!(menu = MENU_GetMenu(hMenu)))
Alexandre Julliard's avatar
Alexandre Julliard committed
1720 1721 1722 1723
        return NULL;

    /* Find where to insert new item */

1724
    if (flags & MF_BYPOSITION) {
1725
        if (pos > menu->nItems)
1726
            pos = menu->nItems;
1727
    } else {
1728
        if (!MENU_FindItem( &hMenu, &pos, flags ))
1729 1730 1731 1732
            pos = menu->nItems;
        else {
            if (!(menu = MENU_GetMenu( hMenu )))
                return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1733 1734 1735 1736 1737
        }
    }

    /* Create new items array */

1738
    newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1739
    if (!newItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1740
    {
1741
        WARN("allocation failed\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1742 1743 1744 1745
        return NULL;
    }
    if (menu->nItems > 0)
    {
1746
	  /* Copy the old array into the new one */
Alexandre Julliard's avatar
Alexandre Julliard committed
1747 1748
	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
1749
					(menu->nItems-pos)*sizeof(MENUITEM) );
1750
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
1751
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1752
    menu->items = newItems;
Alexandre Julliard's avatar
Alexandre Julliard committed
1753
    menu->nItems++;
Alexandre Julliard's avatar
Alexandre Julliard committed
1754
    memset( &newItems[pos], 0, sizeof(*newItems) );
1755
    menu->Height = 0; /* force size recalculate */
Alexandre Julliard's avatar
Alexandre Julliard committed
1756 1757 1758 1759 1760 1761 1762
    return &newItems[pos];
}


/**********************************************************************
 *         MENU_ParseResource
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1763
 * Parse a standard menu resource and add items to the menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
1764
 * Return a pointer to the end of the resource.
1765 1766
 *
 * NOTE: flags is equivalent to the mtOption field
Alexandre Julliard's avatar
Alexandre Julliard committed
1767
 */
1768
static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1769 1770
{
    WORD flags, id = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1771
    LPCSTR str;
Alexandre Julliard's avatar
Alexandre Julliard committed
1772 1773 1774

    do
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1775
        flags = GET_WORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
1776 1777 1778
        res += sizeof(WORD);
        if (!(flags & MF_POPUP))
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1779
            id = GET_WORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
1780 1781
            res += sizeof(WORD);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1782 1783
        str = res;
        if (!unicode) res += strlen(str) + 1;
1784
        else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
1785 1786
        if (flags & MF_POPUP)
        {
1787
            HMENU hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
1788 1789 1790
            if (!hSubMenu) return NULL;
            if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
                return NULL;
1791 1792
            if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
            else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
1793
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1794
        else  /* Not a popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
1795
        {
1796 1797
            if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
            else AppendMenuW( hMenu, flags, id,
Alexandre Julliard's avatar
Alexandre Julliard committed
1798
                                *(LPCWSTR)str ? (LPCWSTR)str : NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1799 1800 1801 1802 1803 1804
        }
    } while (!(flags & MF_END));
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1805 1806 1807 1808 1809 1810
/**********************************************************************
 *         MENUEX_ParseResource
 *
 * Parse an extended menu resource and add items to the menu.
 * Return a pointer to the end of the resource.
 */
1811
static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
1812 1813
{
    WORD resinfo;
Alexandre Julliard's avatar
Alexandre Julliard committed
1814
    do {
1815
	MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
1816

Alexandre Julliard's avatar
Alexandre Julliard committed
1817 1818 1819
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
	mii.fType = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
1820
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
1821
	mii.fState = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
1822
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
1823
	mii.wID = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
1824 1825 1826 1827 1828
        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.  */
	res += (~((int)res - 1)) & 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1829
	mii.dwTypeData = (LPWSTR) res;
1830
	res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
1831 1832 1833
	/* Align the following fields on a dword boundary.  */
	res += (~((int)res - 1)) & 3;

1834 1835
        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
1836

Alexandre Julliard's avatar
Alexandre Julliard committed
1837
	if (resinfo & 1) {	/* Pop-up? */
1838
	    /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
1839
	    res += sizeof(DWORD);
1840
	    mii.hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
1841 1842 1843
	    if (!mii.hSubMenu)
		return NULL;
	    if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1844
		DestroyMenu(mii.hSubMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
1845
                return NULL;
1846
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1847 1848
	    mii.fMask |= MIIM_SUBMENU;
	    mii.fType |= MF_POPUP;
Alexandre Julliard's avatar
Alexandre Julliard committed
1849
        }
1850 1851 1852 1853 1854 1855
	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;
	}
1856
	InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
Alexandre Julliard's avatar
Alexandre Julliard committed
1857 1858 1859 1860 1861
    } while (!(resinfo & MF_END));
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1862 1863 1864 1865 1866
/***********************************************************************
 *           MENU_GetSubPopup
 *
 * Return the handle of the selected sub-popup menu (if any).
 */
1867
static HMENU MENU_GetSubPopup( HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1868
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1869 1870 1871
    POPUPMENU *menu;
    MENUITEM *item;

1872
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1873

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1876
    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
1877 1878
    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
        return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1879
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1880 1881 1882
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1883 1884 1885 1886 1887
/***********************************************************************
 *           MENU_HideSubPopups
 *
 * Hide the sub-popup menus of this menu.
 */
1888 1889
static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
                                BOOL sendMenuSelect )
Alexandre Julliard's avatar
Alexandre Julliard committed
1890
{
1891
    POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1892

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

1895
    if (menu && top_popup)
Alexandre Julliard's avatar
Alexandre Julliard committed
1896
    {
1897
	HMENU hsubmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1898 1899 1900 1901 1902 1903
	POPUPMENU *submenu;
	MENUITEM *item;

	if (menu->FocusedItem != NO_SELECTED_ITEM)
	{
	    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
1904 1905 1906 1907
	    if (!(item->fType & MF_POPUP) ||
		!(item->fState & MF_MOUSESELECT)) return;
	    item->fState &= ~MF_MOUSESELECT;
	    hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1908 1909
	} else return;

1910
	submenu = MENU_GetMenu( hsubmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1911
	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1912
	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1913 1914
        DestroyWindow( submenu->hWnd );
        submenu->hWnd = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1915
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1916 1917 1918
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1919 1920 1921 1922 1923 1924
/***********************************************************************
 *           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.
 */
1925
static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
1926
                                  BOOL selectFirst, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
1927
{
1928
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1929 1930
    POPUPMENU *menu;
    MENUITEM *item;
1931
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1932

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1939
    item = &menu->items[menu->FocusedItem];
1940
    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
1941
        return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
1942

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

1946 1947
    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
1948
       SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
1949
                     MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
Alexandre Julliard's avatar
Alexandre Julliard committed
1950

Alexandre Julliard's avatar
Alexandre Julliard committed
1951 1952 1953
    item = &menu->items[menu->FocusedItem];
    rect = item->rect;

1954
    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
1955
    if (!(item->fState & MF_HILITE))
Alexandre Julliard's avatar
Alexandre Julliard committed
1956
    {
1957 1958
        if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
        else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1959 1960 1961

        SelectObject( hdc, hMenuFont);

Alexandre Julliard's avatar
Alexandre Julliard committed
1962
        item->fState |= MF_HILITE;
1963
        MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
1964
	ReleaseDC( menu->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1965 1966 1967 1968
    }
    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
      item->rect = rect;

Alexandre Julliard's avatar
Alexandre Julliard committed
1969
    item->fState |= MF_MOUSESELECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
1970 1971

    if (IS_SYSTEM_MENU(menu))
Alexandre Julliard's avatar
Alexandre Julliard committed
1972
    {
1973
	MENU_InitSysMenuPopup(item->hSubMenu,
1974 1975
                              GetWindowLongW( menu->hWnd, GWL_STYLE ),
                              GetClassLongW( menu->hWnd, GCL_STYLE));
Alexandre Julliard's avatar
Alexandre Julliard committed
1976

1977
	NC_GetSysPopupPos( menu->hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1978
	rect.top = rect.bottom;
1979 1980
	rect.right = GetSystemMetrics(SM_CXSIZE);
        rect.bottom = GetSystemMetrics(SM_CYSIZE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1981 1982 1983
    }
    else
    {
1984
        GetWindowRect( menu->hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1985 1986
	if (menu->wFlags & MF_POPUP)
	{
1987 1988
	    rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
	    rect.top += item->rect.top;
1989
	    rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
Alexandre Julliard's avatar
Alexandre Julliard committed
1990 1991 1992 1993
	    rect.bottom = item->rect.top - item->rect.bottom;
	}
	else
	{
1994 1995
	    rect.left += item->rect.left;
	    rect.top += item->rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1996 1997 1998
	    rect.right = item->rect.right - item->rect.left;
	    rect.bottom = item->rect.bottom - item->rect.top;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1999
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2000

Alexandre Julliard's avatar
Alexandre Julliard committed
2001
    MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
Alexandre Julliard's avatar
Alexandre Julliard committed
2002
		    rect.left, rect.top, rect.right, rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
2003
    if (selectFirst)
Alexandre Julliard's avatar
Alexandre Julliard committed
2004 2005
        MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
    return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2006
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2007

2008 2009 2010 2011 2012 2013 2014


/**********************************************************************
 *         MENU_IsMenuActive
 */
BOOL MENU_IsMenuActive(void)
{
2015
    return (top_popup != 0);
2016 2017
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2018
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2019
 *           MENU_PtMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2020
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2021 2022
 * Walks menu chain trying to find a menu pt maps to.
 */
2023
static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
2024
{
2025
   POPUPMENU *menu = MENU_GetMenu( hMenu );
2026 2027
   UINT item = menu->FocusedItem;
   HMENU ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2028 2029

   /* try subpopup first (if any) */
2030 2031 2032 2033
   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
2034

2035
   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
Alexandre Julliard's avatar
Alexandre Julliard committed
2036
   {
2037 2038 2039 2040 2041 2042 2043 2044 2045
       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
2046
   }
2047
   return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2048
}
2049

Alexandre Julliard's avatar
Alexandre Julliard committed
2050 2051 2052 2053
/***********************************************************************
 *           MENU_ExecFocusedItem
 *
 * Execute a menu item (for instance when user pressed Enter).
2054
 * Return the wID of the executed item. Otherwise, -1 indicating
2055
 * that no menu item was executed;
2056 2057 2058
 * Have to receive the flags for the TrackPopupMenu options to avoid
 * sending unwanted message.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2059
 */
2060
static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2061 2062
{
    MENUITEM *item;
2063
    POPUPMENU *menu = MENU_GetMenu( hMenu );
2064

2065
    TRACE("%p hmenu=%p\n", pmt, hMenu);
2066

2067
    if (!menu || !menu->nItems ||
2068
	(menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2069

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

2072
    TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
2073 2074

    if (!(item->fType & MF_POPUP))
Alexandre Julliard's avatar
Alexandre Julliard committed
2075
    {
2076
	if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2077
	{
2078 2079
	    /* If TPM_RETURNCMD is set you return the id, but
	       do not send a message to the owner */
2080
	    if(!(wFlags & TPM_RETURNCMD))
Alexandre Julliard's avatar
Alexandre Julliard committed
2081
	    {
2082
		if( menu->wFlags & MF_SYSMENU )
2083
		    PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2084 2085
				  MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
		else
2086
		    PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2087
	    }
2088
	    return item->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
2089
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2090 2091
    }
    else
2092
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2093

2094
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2095 2096
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2097
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2098
 *           MENU_SwitchTracking
Alexandre Julliard's avatar
Alexandre Julliard committed
2099
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2100
 * Helper function for menu navigation routines.
Alexandre Julliard's avatar
Alexandre Julliard committed
2101
 */
2102
static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
Alexandre Julliard's avatar
Alexandre Julliard committed
2103
{
2104 2105
    POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2106

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2109 2110
    if( pmt->hTopMenu != hPtMenu &&
	!((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2111
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2112 2113
	/* both are top level menus (system and menu-bar) */
	MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2114
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2115 2116 2117
        pmt->hTopMenu = hPtMenu;
    }
    else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2118
    MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2119 2120 2121 2122 2123 2124 2125 2126
}


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

Alexandre Julliard's avatar
Alexandre Julliard committed
2131
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2132
    {
2133
	UINT id = 0;
2134
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2135 2136 2137 2138 2139 2140 2141 2142
	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
2143
	{
2144 2145
	    if( ptmenu->FocusedItem != id )
		MENU_SwitchTracking( pmt, hPtMenu, id );
Alexandre Julliard's avatar
Alexandre Julliard committed
2146

2147 2148
	    /* If the popup menu is not already "popped" */
	    if(!(item->fState & MF_MOUSESELECT ))
2149
	    {
2150
		pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2151 2152
	    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2153
	    return TRUE;
2154
	}
2155
	/* Else the click was on the menu bar, finish the tracking */
Alexandre Julliard's avatar
Alexandre Julliard committed
2156
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2157
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2158 2159
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2160 2161 2162
/***********************************************************************
 *           MENU_ButtonUp
 *
2163 2164
 * Return the value of MENU_ExecFocusedItem if
 * the selected item was not a popup. Else open the popup.
2165
 * A -1 return value indicates that we go on with menu tracking.
2166
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2167
 */
2168
static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
2169
{
2170
    TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2171

Alexandre Julliard's avatar
Alexandre Julliard committed
2172
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2173
    {
2174
	UINT id = 0;
2175
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2176
	MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2177

Alexandre Julliard's avatar
Alexandre Julliard committed
2178 2179 2180 2181
        if( IS_SYSTEM_MENU(ptmenu) )
            item = ptmenu->items;
        else
            item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
Alexandre Julliard's avatar
Alexandre Julliard committed
2182

Alexandre Julliard's avatar
Alexandre Julliard committed
2183
	if( item && (ptmenu->FocusedItem == id ))
Alexandre Julliard's avatar
Alexandre Julliard committed
2184
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2185
	    if( !(item->fType & MF_POPUP) )
2186
		return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2187

2188 2189 2190
	    /* 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 */
2191
	    if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2192
		return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2193
	}
2194
	ptmenu->bTimeToHide = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2195
    }
2196
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2197 2198 2199
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2200 2201 2202 2203 2204
/***********************************************************************
 *           MENU_MouseMove
 *
 * Return TRUE if we can go on with menu tracking.
 */
2205
static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2206
{
2207
    UINT id = NO_SELECTED_ITEM;
Alexandre Julliard's avatar
Alexandre Julliard committed
2208
    POPUPMENU *ptmenu = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
2209

Alexandre Julliard's avatar
Alexandre Julliard committed
2210
    if( hPtMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2211
    {
2212
	ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2213 2214 2215 2216
        if( IS_SYSTEM_MENU(ptmenu) )
	    id = 0;
        else
            MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2217
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2218 2219

    if( id == NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
2220
    {
2221
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2222
			 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2223

Alexandre Julliard's avatar
Alexandre Julliard committed
2224
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2225
    else if( ptmenu->FocusedItem != id )
Alexandre Julliard's avatar
Alexandre Julliard committed
2226
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2227
	    MENU_SwitchTracking( pmt, hPtMenu, id );
2228
	    pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2229 2230
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2231 2232
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2233

2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257
/***********************************************************************
 *           MENU_SetCapture
 */
static void MENU_SetCapture( HWND hwnd )
{
    HWND previous = 0;

    SERVER_START_REQ( set_capture_window )
    {
        req->handle = hwnd;
        req->flags  = CAPTURE_MENU;
        if (!wine_server_call_err( req ))
        {
            previous = reply->previous;
            hwnd = reply->full_handle;
        }
    }
    SERVER_END_REQ;

    if (previous && previous != hwnd)
        SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2258 2259
/***********************************************************************
 *           MENU_DoNextMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2260 2261
 *
 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
Alexandre Julliard's avatar
Alexandre Julliard committed
2262
 */
2263
static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
Alexandre Julliard's avatar
Alexandre Julliard committed
2264
{
2265
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2266 2267 2268 2269

    if( (vk == VK_LEFT &&  menu->FocusedItem == 0 ) ||
        (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
    {
2270
        MDINEXTMENU next_menu;
2271 2272 2273
	HMENU hNewMenu;
	HWND  hNewWnd;
	UINT  id = 0;
2274 2275 2276 2277 2278

        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
2279

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

2283
	if (!next_menu.hmenuNext || !next_menu.hwndNext)
Alexandre Julliard's avatar
Alexandre Julliard committed
2284
	{
2285
            DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2286 2287 2288 2289
	    hNewWnd = pmt->hOwnerWnd;
	    if( IS_SYSTEM_MENU(menu) )
	    {
		/* switch to the menu bar */
Alexandre Julliard's avatar
Alexandre Julliard committed
2290

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2293 2294
	        if( vk == VK_LEFT )
	        {
2295
		    menu = MENU_GetMenu( hNewMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2296 2297 2298
		    id = menu->nItems - 1;
	        }
	    }
2299
	    else if (style & WS_SYSMENU )
Alexandre Julliard's avatar
Alexandre Julliard committed
2300 2301
	    {
		/* switch to the system menu */
2302
	        hNewMenu = get_win_sys_menu( hNewWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2303
	    }
2304
            else return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2305 2306 2307
	}
	else    /* application returned a new menu to switch to */
	{
2308
            hNewMenu = next_menu.hmenuNext;
2309
            hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
2310

2311
	    if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2312
	    {
2313
                DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2314

2315 2316
		if (style & WS_SYSMENU &&
		    GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2317 2318
		{
	            /* get the real system menu */
2319
		    hNewMenu =  get_win_sys_menu(hNewWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2320
		}
2321
	        else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2322
		{
2323 2324
		    /* FIXME: Not sure what to do here;
		     * perhaps try to track hNewMenu as a popup? */
Alexandre Julliard's avatar
Alexandre Julliard committed
2325

2326
		    TRACE(" -- got confused.\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2327 2328 2329 2330 2331 2332 2333
		    return FALSE;
		}
	    }
	    else return FALSE;
	}

	if( hNewMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2334
	{
2335
	    MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2336
                    FALSE, 0 );
2337
	    if( pmt->hCurrentMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2338
		MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2339 2340
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
2341 2342 2343
	if( hNewWnd != pmt->hOwnerWnd )
	{
	    pmt->hOwnerWnd = hNewWnd;
2344
	    MENU_SetCapture( pmt->hOwnerWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2345
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2346

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2350 2351 2352 2353
	return TRUE;
    }
    return FALSE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2354

Alexandre Julliard's avatar
Alexandre Julliard committed
2355 2356 2357 2358 2359 2360
/***********************************************************************
 *           MENU_SuspendPopup
 *
 * The idea is not to show the popup if the next input message is
 * going to hide it anyway.
 */
2361
static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
Alexandre Julliard's avatar
Alexandre Julliard committed
2362
{
2363
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
2364

Alexandre Julliard's avatar
Alexandre Julliard committed
2365
    msg.hwnd = pmt->hOwnerWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
2366

2367
    PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2368 2369 2370 2371 2372
    pmt->trackFlags |= TF_SKIPREMOVE;

    switch( uMsg )
    {
	case WM_KEYDOWN:
2373
	     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2374 2375
	     if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
	     {
2376 2377
		 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
2378 2379 2380 2381 2382 2383 2384 2385
	         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
2386
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2387

Alexandre Julliard's avatar
Alexandre Julliard committed
2388 2389 2390
    /* failures go through this */
    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2391
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2392

2393 2394 2395
/***********************************************************************
 *           MENU_KeyEscape
 *
2396
 * Handle a VK_ESCAPE key event in a menu.
2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427
 */
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 );
            }

            MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
            pmt->hCurrentMenu = hmenuprev;
            bEndMenu = FALSE;
        }
    }

    return bEndMenu;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2428 2429 2430
/***********************************************************************
 *           MENU_KeyLeft
 *
2431
 * Handle a VK_LEFT key event in a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2432
 */
2433
static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2434
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2435
    POPUPMENU *menu;
2436 2437
    HMENU hmenutmp, hmenuprev;
    UINT  prevcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2438

Alexandre Julliard's avatar
Alexandre Julliard committed
2439
    hmenuprev = hmenutmp = pmt->hTopMenu;
2440
    menu = MENU_GetMenu( hmenutmp );
Alexandre Julliard's avatar
Alexandre Julliard committed
2441

Alexandre Julliard's avatar
Alexandre Julliard committed
2442
    /* Try to move 1 column left (if possible) */
2443
    if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2444
	NO_SELECTED_ITEM ) {
2445

Alexandre Julliard's avatar
Alexandre Julliard committed
2446
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2447
			 prevcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2448 2449 2450
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2451 2452
    /* close topmost popup */
    while (hmenutmp != pmt->hCurrentMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2453
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2454
	hmenuprev = hmenutmp;
Alexandre Julliard's avatar
Alexandre Julliard committed
2455 2456
	hmenutmp = MENU_GetSubPopup( hmenuprev );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2457

Alexandre Julliard's avatar
Alexandre Julliard committed
2458
    MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2459
    pmt->hCurrentMenu = hmenuprev;
Alexandre Julliard's avatar
Alexandre Julliard committed
2460 2461

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2465 2466
	if( !MENU_DoNextMenu( pmt, VK_LEFT) )
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
2467

Alexandre Julliard's avatar
Alexandre Julliard committed
2468
	if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
Alexandre Julliard's avatar
Alexandre Julliard committed
2469
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2470 2471 2472 2473
	   /* A sublevel menu was displayed - display the next one
	    * unless there is another displacement coming up */

	    if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2474 2475
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
						pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2476
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2477
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2478 2479 2480
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2481 2482 2483 2484 2485
/***********************************************************************
 *           MENU_KeyRight
 *
 * Handle a VK_RIGHT key event in a menu.
 */
2486
static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2487
{
2488
    HMENU hmenutmp;
2489
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2490
    UINT  nextcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2491

2492 2493 2494 2495
    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
2496

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2501
	hmenutmp = pmt->hCurrentMenu;
2502
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2503

Alexandre Julliard's avatar
Alexandre Julliard committed
2504 2505
	/* if subpopup was displayed then we are done */
	if (hmenutmp != pmt->hCurrentMenu) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2506
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2507

Alexandre Julliard's avatar
Alexandre Julliard committed
2508
    /* Check to see if there's another column */
2509
    if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2510
	NO_SELECTED_ITEM ) {
2511
	TRACE("Going to %d.\n", nextcol );
Alexandre Julliard's avatar
Alexandre Julliard committed
2512
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2513
			 nextcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2514 2515 2516
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2517
    if (!(menu->wFlags & MF_POPUP))	/* menu bar tracking */
Alexandre Julliard's avatar
Alexandre Julliard committed
2518
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2519
	if( pmt->hCurrentMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2520
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2521 2522 2523 2524 2525 2526 2527 2528 2529 2530
	    MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
	    hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
	} else hmenutmp = 0;

	/* try to move to the next item */
	if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );

	if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
	    if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2531
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2532
						       pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2533 2534
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2535

Alexandre Julliard's avatar
Alexandre Julliard committed
2536 2537 2538 2539 2540
/***********************************************************************
 *           MENU_TrackMenu
 *
 * Menu tracking code.
 */
2541 2542
static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
                            HWND hwnd, const RECT *lprect )
Alexandre Julliard's avatar
Alexandre Julliard committed
2543
{
2544
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
2545
    POPUPMENU *menu;
2546
    BOOL fRemove;
2547
    INT executedMenuId = -1;
2548
    MTRACKER mt;
2549
    BOOL enterIdleSent = FALSE;
2550 2551 2552 2553

    mt.trackFlags = 0;
    mt.hCurrentMenu = hmenu;
    mt.hTopMenu = hmenu;
2554
    mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2555 2556
    mt.pt.x = x;
    mt.pt.y = y;
Alexandre Julliard's avatar
Alexandre Julliard committed
2557

2558
    TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2559 2560
          hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
          (lprect) ? lprect->right : 0,  (lprect) ? lprect->bottom : 0);
2561

Alexandre Julliard's avatar
Alexandre Julliard committed
2562
    fEndMenu = FALSE;
2563 2564
    if (!(menu = MENU_GetMenu( hmenu )))
    {
2565
        WARN("Invalid menu handle %p\n", hmenu);
2566 2567 2568
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2569

2570
    if (wFlags & TPM_BUTTONDOWN)
2571 2572 2573
    {
	/* Get the result in order to start the tracking or not */
	fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2574
	fEndMenu = !fRemove;
2575
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2576

2577
    MENU_SetCapture( mt.hOwnerWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2578

Alexandre Julliard's avatar
Alexandre Julliard committed
2579
    while (!fEndMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2580
    {
2581
	menu = MENU_GetMenu( mt.hCurrentMenu );
2582 2583
	if (!menu) /* sometimes happens if I do a window manager close */
	    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
2584

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

2588 2589
        for (;;)
        {
2590
            if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2591
            {
2592
                if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2593
                /* remove the message from the queue */
2594
                PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606
            }
            else
            {
                if (!enterIdleSent)
                {
                    HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
                    enterIdleSent = TRUE;
                    SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
                }
                WaitMessage();
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2607

2608
	/* check if EndMenu() tried to cancel us, by posting this message */
2609
        if(msg.message == WM_CANCELMODE)
2610 2611 2612 2613 2614
	{
	    /* we are now out of the loop */
    	    fEndMenu = TRUE;

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

2617 2618 2619 2620
	    /* break out of internal loop, ala ESCAPE */
	    break;
	}

2621
        TranslateMessage( &msg );
2622
        mt.pt = msg.pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
2623

2624 2625 2626
	if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
	  enterIdleSent=FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
2627
        fRemove = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2628
	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
2629
	{
2630
            /*
2631 2632 2633
             * Use the mouse coordinates in lParam instead of those in the MSG
             * struct to properly handle synthetic messages. They are already
             * in screen coordinates.
2634
             */
2635 2636
            mt.pt.x = (short)LOWORD(msg.lParam);
            mt.pt.y = (short)HIWORD(msg.lParam);
2637

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2641
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
2642
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2643 2644
		/* no WM_NC... messages in captured state */

Alexandre Julliard's avatar
Alexandre Julliard committed
2645 2646 2647 2648 2649 2650
		case WM_RBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONDBLCLK:
		case WM_LBUTTONDOWN:
2651 2652 2653 2654
		    /* 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
2655
		    break;
2656

Alexandre Julliard's avatar
Alexandre Julliard committed
2657 2658 2659 2660
		case WM_RBUTTONUP:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONUP:
2661 2662
		    /* Check if a menu was selected by the mouse */
		    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2663
		    {
2664 2665 2666
                        executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);

			/* End the loop if executedMenuId is an item ID */
2667 2668
			/* or if the job was done (executedMenuId = 0). */
                        fEndMenu = fRemove = (executedMenuId != -1);
Alexandre Julliard's avatar
Alexandre Julliard committed
2669
		    }
2670 2671 2672 2673 2674
                    /* 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);
2675

Alexandre Julliard's avatar
Alexandre Julliard committed
2676
		    break;
2677

Alexandre Julliard's avatar
Alexandre Julliard committed
2678
		case WM_MOUSEMOVE:
2679 2680
                    /* the selected menu item must be changed every time */
		     /* the mouse moves. */
2681

2682
                    if (hmenu)
2683
			fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2684

Alexandre Julliard's avatar
Alexandre Julliard committed
2685
	    } /* switch(msg.message) - mouse */
Alexandre Julliard's avatar
Alexandre Julliard committed
2686
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2687
	else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
2688
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2689
            fRemove = TRUE;  /* Keyboard messages are always removed */
Alexandre Julliard's avatar
Alexandre Julliard committed
2690
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
2691 2692
	    {
	    case WM_KEYDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
2693
		switch(msg.wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
2694 2695 2696
		{
		case VK_HOME:
		case VK_END:
2697
		    MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2698
				     NO_SELECTED_ITEM, FALSE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2699
		/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
2700
		case VK_UP:
2701
		    MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
2702
				       (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
2703 2704
		    break;

Alexandre Julliard's avatar
Alexandre Julliard committed
2705 2706
		case VK_DOWN: /* If on menu bar, pull-down the menu */

2707
		    menu = MENU_GetMenu( mt.hCurrentMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2708
		    if (!(menu->wFlags & MF_POPUP))
2709
			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2710 2711
		    else      /* otherwise try to move selection */
			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
Alexandre Julliard's avatar
Alexandre Julliard committed
2712 2713 2714
		    break;

		case VK_LEFT:
2715
		    MENU_KeyLeft( &mt, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2716
		    break;
2717

Alexandre Julliard's avatar
Alexandre Julliard committed
2718
		case VK_RIGHT:
2719
		    MENU_KeyRight( &mt, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2720
		    break;
2721

Alexandre Julliard's avatar
Alexandre Julliard committed
2722
		case VK_ESCAPE:
2723
                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2724 2725
		    break;

2726 2727 2728 2729 2730
		case VK_F1:
		    {
			HELPINFO hi;
			hi.cbSize = sizeof(HELPINFO);
			hi.iContextType = HELPINFO_MENUITEM;
2731
			if (menu->FocusedItem == NO_SELECTED_ITEM)
2732
			    hi.iCtrlId = 0;
2733 2734
		        else
			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2735 2736 2737
			hi.hItemHandle = hmenu;
			hi.dwContextId = menu->dwContextHelpID;
			hi.MousePos = msg.pt;
2738
			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2739 2740 2741
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2742 2743 2744 2745 2746 2747
		default:
		    break;
		}
		break;  /* WM_KEYDOWN */

	    case WM_SYSKEYDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
2748
		switch(msg.wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
2749 2750
		{
		case VK_MENU:
Alexandre Julliard's avatar
Alexandre Julliard committed
2751
		    fEndMenu = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2752
		    break;
2753

Alexandre Julliard's avatar
Alexandre Julliard committed
2754 2755 2756 2757 2758
		}
		break;  /* WM_SYSKEYDOWN */

	    case WM_CHAR:
		{
2759
		    UINT	pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
2760

2761 2762
		    if (msg.wParam == '\r' || msg.wParam == ' ')
		    {
2763
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2764
                        fEndMenu = (executedMenuId != -1);
2765

2766 2767 2768
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2769 2770
		      /* Hack to avoid control chars. */
		      /* We will find a better way real soon... */
2771
		    if (msg.wParam < 32) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
2772

2773
		    pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2774
                                              LOWORD(msg.wParam), FALSE );
2775 2776
		    if (pos == (UINT)-2) fEndMenu = TRUE;
		    else if (pos == (UINT)-1) MessageBeep(0);
Alexandre Julliard's avatar
Alexandre Julliard committed
2777 2778
		    else
		    {
2779 2780
			MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
                                TRUE, 0 );
2781
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2782
                        fEndMenu = (executedMenuId != -1);
Alexandre Julliard's avatar
Alexandre Julliard committed
2783
		    }
2784
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
2785 2786
		break;
	    }  /* switch(msg.message) - kbd */
Alexandre Julliard's avatar
Alexandre Julliard committed
2787 2788 2789
	}
	else
	{
2790
	    DispatchMessageW( &msg );
Alexandre Julliard's avatar
Alexandre Julliard committed
2791 2792
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
2793 2794 2795 2796 2797
	if (!fEndMenu) fRemove = TRUE;

	/* finally remove message from the queue */

        if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2798
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2799
	else mt.trackFlags &= ~TF_SKIPREMOVE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2800
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2801

2802
    MENU_SetCapture(0);  /* release the capture */
2803

2804 2805 2806 2807 2808
    /* 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
2809
    {
2810
	menu = MENU_GetMenu( mt.hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2811

2812 2813 2814 2815
        if( IsWindow( mt.hOwnerWnd ) )
        {
	    MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );

2816
	    if (menu && menu->wFlags & MF_POPUP)
2817
	    {
2818 2819
                DestroyWindow( menu->hWnd );
                menu->hWnd = 0;
2820 2821
	    }
	    MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2822
	    SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2823 2824 2825 2826
        }

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

2829
    /* The return value is only used by TrackPopupMenu */
2830 2831 2832
    if (!(wFlags & TPM_RETURNCMD)) return TRUE;
    if (executedMenuId == -1) executedMenuId = 0;
    return executedMenuId;
Alexandre Julliard's avatar
Alexandre Julliard committed
2833
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2834

Alexandre Julliard's avatar
Alexandre Julliard committed
2835 2836 2837
/***********************************************************************
 *           MENU_InitTracking
 */
2838
static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
2839
{
2840
    TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2841

2842
    HideCaret(0);
2843 2844 2845

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

2848
    SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2849 2850

    if (!(wFlags & TPM_NONOTIFY))
2851 2852
    {
       POPUPMENU *menu;
2853
       SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2854 2855 2856
       if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
       { /* app changed/recreated menu bar entries in WM_INITMENU
            Recalculate menu sizes else clicks will not work */
2857 2858
          SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                        SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2859

2860 2861
       }
    }
2862 2863 2864 2865 2866 2867 2868
    return TRUE;
}
/***********************************************************************
 *           MENU_ExitTracking
 */
static BOOL MENU_ExitTracking(HWND hWnd)
{
2869
    TRACE("hwnd=%p\n", hWnd);
2870

2871
    SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2872
    ShowCaret(0);
2873
    top_popup = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2874 2875 2876
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2877 2878 2879 2880 2881
/***********************************************************************
 *           MENU_TrackMouseMenuBar
 *
 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
 */
2882
void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
2883
{
2884
    HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2885
    UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
2886

2887
    TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2888

2889
    if (IsMenu(hMenu))
Alexandre Julliard's avatar
Alexandre Julliard committed
2890
    {
2891 2892 2893 2894
        /* map point to parent client coordinates */
        HWND parent = GetAncestor( hWnd, GA_PARENT );
        if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );

2895 2896
	MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
	MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2897
	MENU_ExitTracking(hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2898
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2899 2900 2901 2902 2903 2904 2905 2906
}


/***********************************************************************
 *           MENU_TrackKbdMenuBar
 *
 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
 */
2907
void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
Alexandre Julliard's avatar
Alexandre Julliard committed
2908
{
2909 2910 2911
    UINT uItem = NO_SELECTED_ITEM;
    HMENU hTrackMenu;
    UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
2912

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

Alexandre Julliard's avatar
Alexandre Julliard committed
2915
    /* find window that has a menu */
2916

2917
    while (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)
2918
        if (!(hwnd = GetParent( hwnd ))) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2919

Alexandre Julliard's avatar
Alexandre Julliard committed
2920
    /* check if we have to track a system menu */
2921 2922

    hTrackMenu = GetMenu( hwnd );
2923
    if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
Alexandre Julliard's avatar
Alexandre Julliard committed
2924
    {
2925 2926
        if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
        if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED) return;
2927 2928 2929
        hTrackMenu = get_win_sys_menu( hwnd );
        uItem = 0;
        wParam |= HTSYSMENU; /* prevent item lookup */
Alexandre Julliard's avatar
Alexandre Julliard committed
2930
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2931

2932 2933 2934
    if (!IsMenu( hTrackMenu )) return;

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

2936
    if( wChar && wChar != ' ' )
2937
    {
2938
        uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
2939
        if( uItem >= (UINT)(-2) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2940
        {
2941 2942
            if( uItem == (UINT)(-1) ) MessageBeep(0);
            hTrackMenu = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2943
        }
2944
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2945

2946 2947 2948
    if( hTrackMenu )
    {
        MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2949

2950 2951
        if( uItem == NO_SELECTED_ITEM )
            MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
2952
        else if( wChar )
2953
            PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
2954

2955
        MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2956
    }
2957
    MENU_ExitTracking( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2958 2959 2960
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2961
/**********************************************************************
2962
 *           TrackPopupMenu   (USER32.@)
2963 2964 2965 2966
 *
 * 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
2967
 */
2968 2969
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
2970
{
2971
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2972

2973 2974 2975 2976
    MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);

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

2979
    if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
2980
        ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
2981
    MENU_ExitTracking(hWnd);
2982

Alexandre Julliard's avatar
Alexandre Julliard committed
2983
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2984 2985
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2986
/**********************************************************************
2987
 *           TrackPopupMenuEx   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
2988
 */
2989 2990
BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
                                HWND hWnd, LPTPMPARAMS lpTpm )
Alexandre Julliard's avatar
Alexandre Julliard committed
2991
{
2992
    FIXME("not fully implemented\n" );
2993
    return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
Alexandre Julliard's avatar
Alexandre Julliard committed
2994
                             lpTpm ? &lpTpm->rcExclude : NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2995
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2996

Alexandre Julliard's avatar
Alexandre Julliard committed
2997 2998
/***********************************************************************
 *           PopupMenuWndProc
Alexandre Julliard's avatar
Alexandre Julliard committed
2999 3000
 *
 * NOTE: Windows has totally different (and undocumented) popup wndproc.
Alexandre Julliard's avatar
Alexandre Julliard committed
3001
 */
3002 3003
static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
3004
    TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3005

Alexandre Julliard's avatar
Alexandre Julliard committed
3006 3007 3008 3009
    switch(message)
    {
    case WM_CREATE:
	{
3010 3011 3012
	    CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
	    SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3013 3014 3015
	}

    case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3016
        return MA_NOACTIVATE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3017 3018 3019

    case WM_PAINT:
	{
3020 3021
	    PAINTSTRUCT ps;
	    BeginPaint( hwnd, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
3022
	    MENU_DrawPopupMenu( hwnd, ps.hdc,
3023
                                (HMENU)GetWindowLongW( hwnd, 0 ) );
3024
	    EndPaint( hwnd, &ps );
3025
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3026
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
3027
    case WM_ERASEBKGND:
3028
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3029

Alexandre Julliard's avatar
Alexandre Julliard committed
3030
    case WM_DESTROY:
3031 3032 3033
        /* zero out global pointer in case resident popup window was destroyed. */
        if (hwnd == top_popup) top_popup = 0;
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3034

Alexandre Julliard's avatar
Alexandre Julliard committed
3035 3036 3037 3038
    case WM_SHOWWINDOW:

	if( wParam )
	{
3039
            if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
3040 3041
	}
	else
3042
            SetWindowLongW( hwnd, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3043 3044 3045
	break;

    case MM_SETMENUHANDLE:
3046
        SetWindowLongW( hwnd, 0, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3047
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3048 3049

    case MM_GETMENUHANDLE:
3050
        return GetWindowLongW( hwnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3051

Alexandre Julliard's avatar
Alexandre Julliard committed
3052
    default:
3053
        return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3054
    }
3055
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3056
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3057

Alexandre Julliard's avatar
Alexandre Julliard committed
3058

Alexandre Julliard's avatar
Alexandre Julliard committed
3059 3060 3061 3062 3063
/***********************************************************************
 *           MENU_GetMenuBarHeight
 *
 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
 */
3064 3065
UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
                              INT orgX, INT orgY )
Alexandre Julliard's avatar
Alexandre Julliard committed
3066
{
3067 3068
    HDC hdc;
    RECT rectBar;
Alexandre Julliard's avatar
Alexandre Julliard committed
3069 3070
    LPPOPUPMENU lppop;

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

3073
    if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3074

3075
    hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3076
    SelectObject( hdc, hMenuFont);
3077
    SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3078
    MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3079
    ReleaseDC( hwnd, hdc );
3080
    return lppop->Height;
Alexandre Julliard's avatar
Alexandre Julliard committed
3081 3082 3083
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3084
/*******************************************************************
3085
 *         ChangeMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3086
 */
3087 3088
BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3089
{
3090
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3091
    if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3092
                                                 id, data );
3093 3094
    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
3095
                                                id, data );
3096
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3097 3098 3099
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3100
    return InsertMenuA( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3101 3102 3103 3104
}


/*******************************************************************
3105
 *         ChangeMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3106
 */
3107 3108
BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3109
{
3110
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3111
    if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3112
                                                 id, data );
3113 3114
    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
3115
                                                id, data );
3116
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3117 3118 3119
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3120
    return InsertMenuW( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3121
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3122 3123


Alexandre Julliard's avatar
Alexandre Julliard committed
3124
/*******************************************************************
3125
 *         CheckMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3126
 */
3127
DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3128
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3129
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3130
    DWORD ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3131

3132
    TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3133
    if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3134 3135 3136
    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
3137
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3138 3139 3140
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3141
/**********************************************************************
3142
 *         EnableMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3143
 */
3144
UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3145
{
3146
    UINT    oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3147
    MENUITEM *item;
3148
    POPUPMENU *menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3149

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

3152
    /* Get the Popupmenu to access the owner menu */
3153
    if (!(menu = MENU_GetMenu(hMenu)))
3154 3155
	return (UINT)-1;

Alexandre Julliard's avatar
Alexandre Julliard committed
3156
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3157
	return (UINT)-1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3158 3159 3160

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

3162 3163 3164 3165
    /* If the close item in the system menu change update the close button */
    if((item->wID == SC_CLOSE) && (oldflags != wFlags))
    {
	if (menu->hSysMenuOwner != 0)
3166
	{
3167
	    POPUPMENU* parentMenu;
3168

3169 3170 3171
	    /* Get the parent menu to access*/
	    if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
		return (UINT)-1;
3172

3173 3174 3175
	    /* Refresh the frame to reflect the change*/
	    SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
		         SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3176
	}
3177
    }
3178

Alexandre Julliard's avatar
Alexandre Julliard committed
3179
    return oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3180 3181 3182
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3183
/*******************************************************************
3184
 *         GetMenuStringA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3185
 */
3186
INT WINAPI GetMenuStringA(
3187 3188 3189 3190
	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*/
3191
	UINT wFlags	/* [in] MF_ flags */
3192
) {
Alexandre Julliard's avatar
Alexandre Julliard committed
3193
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3194

3195
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3196
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3197
    if (!IS_STRING_ITEM(item->fType)) return 0;
3198
    if (!str || !nMaxSiz) return strlenW(item->text);
3199
    str[0] = '\0';
3200 3201
    if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
        str[nMaxSiz-1] = 0;
3202
    TRACE("returning '%s'\n", str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3203
    return strlen(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3204 3205 3206
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3207
/*******************************************************************
3208
 *         GetMenuStringW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3209
 */
3210 3211
INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
                               LPWSTR str, INT nMaxSiz, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3212 3213 3214
{
    MENUITEM *item;

3215
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3216
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3217
    if (!IS_STRING_ITEM(item->fType)) return 0;
3218
    if (!str || !nMaxSiz) return strlenW(item->text);
3219
    str[0] = '\0';
3220 3221
    lstrcpynW( str, item->text, nMaxSiz );
    return strlenW(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3222 3223 3224
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3225
/**********************************************************************
3226
 *         HiliteMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3227
 */
3228 3229
BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
                                UINT wHilite )
Alexandre Julliard's avatar
Alexandre Julliard committed
3230
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3231
    LPPOPUPMENU menu;
3232
    TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
Alexandre Julliard's avatar
Alexandre Julliard committed
3233
    if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3234
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3235
    if (menu->FocusedItem == wItemID) return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3236
    MENU_HideSubPopups( hWnd, hMenu, FALSE );
3237
    MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3238
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3239 3240 3241
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3242
/**********************************************************************
3243
 *         GetMenuState    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3244
 */
3245
UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3246
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3247
    MENUITEM *item;
3248
    TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3249
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3250
    debug_print_menuitem ("  item: ", item, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
3251
    if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
3252
    {
3253
	POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3254
	if (!menu) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3255
	else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
Alexandre Julliard's avatar
Alexandre Julliard committed
3256
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3257
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
3258
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3259 3260 3261 3262
	/* 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
3263
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3264 3265 3266
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3267
/**********************************************************************
3268
 *         GetMenuItemCount    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3269
 */
3270
INT WINAPI GetMenuItemCount( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3271
{
3272 3273
    LPPOPUPMENU	menu = MENU_GetMenu(hMenu);
    if (!menu) return -1;
3274
    TRACE("(%p) returning %d\n", hMenu, menu->nItems );
Alexandre Julliard's avatar
Alexandre Julliard committed
3275 3276 3277
    return menu->nItems;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3278 3279

/**********************************************************************
3280
 *         GetMenuItemID    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3281
 */
3282
UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
3283
{
3284
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
3285

3286
    if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3287 3288
    if (lpmi->fType & MF_POPUP) return -1;
    return lpmi->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
3289

3290
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3291

Alexandre Julliard's avatar
Alexandre Julliard committed
3292

Alexandre Julliard's avatar
Alexandre Julliard committed
3293
/*******************************************************************
3294
 *         InsertMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3295
 */
3296
BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3297
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3298
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3299
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3300

Alexandre Julliard's avatar
Alexandre Julliard committed
3301
    if (IS_STRING_ITEM(flags) && str)
3302 3303 3304 3305
        TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
              hMenu, pos, flags, id, debugstr_w(str) );
    else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
               hMenu, pos, flags, id, (DWORD)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3306

Alexandre Julliard's avatar
Alexandre Julliard committed
3307
    if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3308

Alexandre Julliard's avatar
Alexandre Julliard committed
3309
    if (!(MENU_SetItemData( item, flags, id, str )))
Alexandre Julliard's avatar
Alexandre Julliard committed
3310
    {
3311
        RemoveMenu( hMenu, pos, flags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3312 3313
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3314

Alexandre Julliard's avatar
Alexandre Julliard committed
3315
    if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3316
	(MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
Alexandre Julliard's avatar
Alexandre Julliard committed
3317

Alexandre Julliard's avatar
Alexandre Julliard committed
3318
    item->hCheckBit = item->hUnCheckBit = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3319
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3320 3321 3322
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3323
/*******************************************************************
3324
 *         InsertMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3325
 */
3326
BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3327
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3328
{
3329
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3330 3331 3332

    if (IS_STRING_ITEM(flags) && str)
    {
3333 3334 3335 3336 3337 3338 3339 3340
        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
3341 3342
        return ret;
    }
3343
    else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3344 3345 3346 3347
}


/*******************************************************************
3348
 *         AppendMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3349
 */
3350
BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3351
                         UINT_PTR id, LPCSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3352
{
3353
    return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3354 3355 3356 3357
}


/*******************************************************************
3358
 *         AppendMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3359
 */
3360
BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3361
                         UINT_PTR id, LPCWSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3362
{
3363
    return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3364 3365 3366
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3367
/**********************************************************************
3368
 *         RemoveMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3369
 */
3370
BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3371
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3372
    LPPOPUPMENU	menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3373 3374
    MENUITEM *item;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
3379 3380
      /* Remove item */

Alexandre Julliard's avatar
Alexandre Julliard committed
3381 3382
    MENU_FreeItemData( item );

Alexandre Julliard's avatar
Alexandre Julliard committed
3383 3384
    if (--menu->nItems == 0)
    {
3385
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
3386
        menu->items = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3387 3388 3389 3390 3391
    }
    else
    {
	while(nPos < menu->nItems)
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
3392 3393
	    *item = *(item+1);
	    item++;
Alexandre Julliard's avatar
Alexandre Julliard committed
3394 3395
	    nPos++;
	}
3396
        menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
Alexandre Julliard's avatar
Alexandre Julliard committed
3397
                                   menu->nItems * sizeof(MENUITEM) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3398 3399
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3400 3401 3402
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3403
/**********************************************************************
3404
 *         DeleteMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3405
 */
3406
BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3407
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3408 3409
    MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
    if (!item) return FALSE;
3410
    if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3411
      /* nPos is now the position of the item */
3412
    RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
Alexandre Julliard's avatar
Alexandre Julliard committed
3413 3414 3415 3416
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3417
/*******************************************************************
3418
 *         ModifyMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3419
 */
3420
BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3421
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3422
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3423 3424 3425
    MENUITEM *item;

    if (IS_STRING_ITEM(flags))
Alexandre Julliard's avatar
Alexandre Julliard committed
3426
    {
3427
        TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3428
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3429
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
3430
    {
3431
        TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3432 3433
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3434
    if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3435 3436 3437 3438 3439
    return MENU_SetItemData( item, flags, id, str );
}


/*******************************************************************
3440
 *         ModifyMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3441
 */
3442
BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3443
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3444
{
3445
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3446

Alexandre Julliard's avatar
Alexandre Julliard committed
3447 3448
    if (IS_STRING_ITEM(flags) && str)
    {
3449 3450 3451 3452 3453 3454 3455 3456
        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
3457 3458
        return ret;
    }
3459
    else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3460 3461 3462
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3463
/**********************************************************************
3464
 *         CreatePopupMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3465
 */
3466
HMENU WINAPI CreatePopupMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3467
{
3468
    HMENU hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3469 3470
    POPUPMENU *menu;

3471
    if (!(hmenu = CreateMenu())) return 0;
3472
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3473
    menu->wFlags |= MF_POPUP;
3474
    menu->bTimeToHide = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3475
    return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3476 3477 3478 3479
}


/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
3480 3481
 *         GetMenuCheckMarkDimensions    (USER.417)
 *         GetMenuCheckMarkDimensions    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3482
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3483
DWORD WINAPI GetMenuCheckMarkDimensions(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3484
{
3485
    return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3486 3487 3488
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3489
/**********************************************************************
3490
 *         SetMenuItemBitmaps    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3491
 */
3492 3493
BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
                                    HBITMAP hNewUnCheck, HBITMAP hNewCheck)
Alexandre Julliard's avatar
Alexandre Julliard committed
3494
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3495
    MENUITEM *item;
3496 3497
    TRACE("(%p, %04x, %04x, %p, %p)\n",
          hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
Alexandre Julliard's avatar
Alexandre Julliard committed
3498
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3499 3500 3501

    if (!hNewCheck && !hNewUnCheck)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3502
	item->fState &= ~MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
3503 3504 3505
    }
    else  /* Install new bitmaps */
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3506 3507 3508
	item->hCheckBit = hNewCheck;
	item->hUnCheckBit = hNewUnCheck;
	item->fState |= MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
3509 3510
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3511 3512 3513
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3514
/**********************************************************************
3515
 *         CreateMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3516
 */
3517
HMENU WINAPI CreateMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3518
{
3519
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3520
    LPPOPUPMENU menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3521
    if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3522
    menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3523 3524

    ZeroMemory(menu, sizeof(POPUPMENU));
Alexandre Julliard's avatar
Alexandre Julliard committed
3525 3526
    menu->wMagic = MENU_MAGIC;
    menu->FocusedItem = NO_SELECTED_ITEM;
3527
    menu->bTimeToHide = FALSE;
3528

3529
    TRACE("return %p\n", hMenu );
3530

Alexandre Julliard's avatar
Alexandre Julliard committed
3531
    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3532 3533 3534
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3535
/**********************************************************************
3536
 *         DestroyMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3537
 */
3538
BOOL WINAPI DestroyMenu( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3539
{
3540
    TRACE("(%p)\n", hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
3541

Alexandre Julliard's avatar
Alexandre Julliard committed
3542 3543 3544 3545
    /* Silently ignore attempts to destroy default system popup */

    if (hMenu && hMenu != MENU_DefSysPopup)
    {
3546
        LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
3547

3548
        if (!lppop) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3549

3550
        lppop->wMagic = 0;  /* Mark it as destroyed */
Alexandre Julliard's avatar
Alexandre Julliard committed
3551

3552
        if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3553
        {
3554
            DestroyWindow( lppop->hWnd );
3555 3556
            lppop->hWnd = 0;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3557

3558
        if (lppop->items) /* recursively destroy submenus */
3559
        {
3560 3561 3562 3563 3564 3565 3566 3567
            int i;
            MENUITEM *item = lppop->items;
            for (i = lppop->nItems; i > 0; i--, item++)
            {
                if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
                MENU_FreeItemData( item );
            }
            HeapFree( GetProcessHeap(), 0, lppop->items );
3568
        }
3569
        USER_HEAP_FREE( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3570
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3571
    return (hMenu != MENU_DefSysPopup);
Alexandre Julliard's avatar
Alexandre Julliard committed
3572 3573
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3574

Alexandre Julliard's avatar
Alexandre Julliard committed
3575
/**********************************************************************
3576
 *         GetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3577
 */
3578
HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
Alexandre Julliard's avatar
Alexandre Julliard committed
3579
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3580
    WND *wndPtr = WIN_FindWndPtr( hWnd );
3581
    HMENU retvalue = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3582

Alexandre Julliard's avatar
Alexandre Julliard committed
3583
    if (wndPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
3584
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3585 3586 3587 3588
	if( wndPtr->hSysMenu )
	{
	    if( bRevert )
	    {
3589
		DestroyMenu(wndPtr->hSysMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
3590 3591 3592 3593
		wndPtr->hSysMenu = 0;
	    }
	    else
	    {
3594
		POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3595
		if( menu )
3596 3597 3598 3599
                {
		   if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
		      menu->items[0].hSubMenu = MENU_CopySysPopup();
		}
3600
		else
3601
		{
3602
		   WARN("Current sys-menu (%p) of wnd %p is broken\n",
3603 3604 3605
			wndPtr->hSysMenu, hWnd);
		   wndPtr->hSysMenu = 0;
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
3606 3607 3608 3609
	    }
	}

	if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3610
	    wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
Alexandre Julliard's avatar
Alexandre Julliard committed
3611 3612

	if( wndPtr->hSysMenu )
3613
        {
3614
	    POPUPMENU *menu;
3615
	    retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3616 3617 3618

	    /* Store the dummy sysmenu handle to facilitate the refresh */
	    /* of the close button if the SC_CLOSE item change */
3619 3620
	    menu = MENU_GetMenu(retvalue);
	    if ( menu )
3621
	       menu->hSysMenuOwner = wndPtr->hSysMenu;
3622 3623
        }
        WIN_ReleaseWndPtr(wndPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
3624
    }
3625
    return bRevert ? 0 : retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
3626 3627
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3628

Alexandre Julliard's avatar
Alexandre Julliard committed
3629
/*******************************************************************
3630
 *         SetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3631
 */
3632
BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3633
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3634
    WND *wndPtr = WIN_FindWndPtr(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3635

Alexandre Julliard's avatar
Alexandre Julliard committed
3636 3637
    if (wndPtr)
    {
3638
	if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3639
	wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3640
        WIN_ReleaseWndPtr(wndPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
3641 3642 3643
	return TRUE;
    }
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3644 3645
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3646

Alexandre Julliard's avatar
Alexandre Julliard committed
3647
/**********************************************************************
3648
 *         GetMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3649
 */
3650
HMENU WINAPI GetMenu( HWND hWnd )
3651
{
3652
    HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3653
    TRACE("for %p returning %p\n", hWnd, retvalue);
3654
    return retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
3655 3656
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3657

Alexandre Julliard's avatar
Alexandre Julliard committed
3658
/**********************************************************************
3659 3660 3661 3662
 *         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
3663
 */
3664
BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3665
{
3666
    TRACE("(%p, %p);\n", hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
3667

3668 3669
    if (hMenu && !IsMenu(hMenu))
    {
3670
        WARN("hMenu %p is not a menu handle\n", hMenu);
3671
        return FALSE;
3672
    }
3673
    if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3674

3675
    hWnd = WIN_GetFullHandle( hWnd );
3676
    if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
Alexandre Julliard's avatar
Alexandre Julliard committed
3677

3678 3679 3680
    if (hMenu != 0)
    {
        LPPOPUPMENU lpmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3681

3682
        if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3683

3684 3685
        lpmenu->hWnd = hWnd;
        lpmenu->Height = 0;  /* Make sure we recalculate the size */
Alexandre Julliard's avatar
Alexandre Julliard committed
3686
    }
3687
    SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3688
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3689 3690 3691
}


3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704
/**********************************************************************
 *         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
3705

Alexandre Julliard's avatar
Alexandre Julliard committed
3706
/**********************************************************************
3707
 *         GetSubMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3708
 */
3709
HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
3710
{
3711
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
3712

3713 3714 3715
    if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
    if (!(lpmi->fType & MF_POPUP)) return 0;
    return lpmi->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3716 3717 3718
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3719
/**********************************************************************
3720
 *         DrawMenuBar    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3721
 */
3722
BOOL WINAPI DrawMenuBar( HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
3723 3724
{
    LPPOPUPMENU lppop;
3725
    HMENU hMenu = GetMenu(hWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
3726

3727
    if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3728 3729 3730 3731 3732 3733 3734
    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
3735 3736
}

3737 3738
/***********************************************************************
 *           DrawMenuBarTemp   (USER32.@)
3739 3740
 *
 * UNDOCUMENTED !!
3741
 *
3742 3743 3744
 * called by W98SE desk.cpl Control Panel Applet
 *
 * Not 100% sure about the param names, but close.
3745
 */
3746
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3747
{
3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775
    LPPOPUPMENU lppop;
    UINT i,retvalue;
    HFONT hfontOld = 0;

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

    if (!hFont)
        hFont = hMenuFont;

    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;

    FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );

3776 3777 3778
    SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
    MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
    LineTo( hDC, lprect->right, lprect->bottom );
3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795

    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;
3796
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3797

Alexandre Julliard's avatar
Alexandre Julliard committed
3798
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
3799 3800
 *           EndMenu   (USER.187)
 *           EndMenu   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3801
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3802
void WINAPI EndMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
3803
{
3804
    /* if we are in the menu code, and it is active */
3805
    if (!fEndMenu && top_popup)
3806 3807 3808 3809 3810 3811 3812 3813
    {
	/* 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 */
3814
        PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3815
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3816 3817 3818 3819 3820 3821
}


/***********************************************************************
 *           LookupMenuHandle   (USER.217)
 */
3822
HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
Alexandre Julliard's avatar
Alexandre Julliard committed
3823
{
3824
    HMENU hmenu32 = HMENU_32(hmenu);
3825
    UINT id32 = id;
Alexandre Julliard's avatar
Alexandre Julliard committed
3826
    if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3827
    else return HMENU_16(hmenu32);
Alexandre Julliard's avatar
Alexandre Julliard committed
3828 3829 3830
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3831
/**********************************************************************
3832
 *	    LoadMenu    (USER.150)
Alexandre Julliard's avatar
Alexandre Julliard committed
3833
 */
3834
HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
3835
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3836 3837 3838
    HRSRC16 hRsrc;
    HGLOBAL16 handle;
    HMENU16 hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3839

3840
    if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3841
    if (!name) return 0;
3842

Alexandre Julliard's avatar
Alexandre Julliard committed
3843
    instance = GetExePtr( instance );
3844
    if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3845 3846 3847
    if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
    hMenu = LoadMenuIndirect16(LockResource16(handle));
    FreeResource16( handle );
Alexandre Julliard's avatar
Alexandre Julliard committed
3848 3849 3850 3851
    return hMenu;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3852
/*****************************************************************
3853
 *        LoadMenuA   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3854
 */
3855
HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
3856
{
3857
    HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
3858
    if (!hrsrc) return 0;
3859
    return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
3860 3861 3862 3863
}


/*****************************************************************
3864
 *        LoadMenuW   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3865
 */
3866
HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
3867
{
3868
    HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
3869
    if (!hrsrc) return 0;
3870
    return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
3871 3872 3873
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3874
/**********************************************************************
3875
 *	    LoadMenuIndirect    (USER.220)
Alexandre Julliard's avatar
Alexandre Julliard committed
3876
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3877
HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
3878
{
3879
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3880 3881
    WORD version, offset;
    LPCSTR p = (LPCSTR)template;
Alexandre Julliard's avatar
Alexandre Julliard committed
3882

3883
    TRACE("(%p)\n", template );
Alexandre Julliard's avatar
Alexandre Julliard committed
3884 3885 3886 3887
    version = GET_WORD(p);
    p += sizeof(WORD);
    if (version)
    {
3888
        WARN("version must be 0 for Win16\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
3889 3890 3891 3892
        return 0;
    }
    offset = GET_WORD(p);
    p += sizeof(WORD) + offset;
3893
    if (!(hMenu = CreateMenu())) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3894
    if (!MENU_ParseResource( p, hMenu, FALSE ))
Alexandre Julliard's avatar
Alexandre Julliard committed
3895
    {
3896
        DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3897
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3898
    }
3899
    return HMENU_16(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
3900 3901
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3902

Alexandre Julliard's avatar
Alexandre Julliard committed
3903
/**********************************************************************
3904
 *	    LoadMenuIndirectW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3905
 */
3906
HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
3907
{
3908
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3909 3910 3911 3912 3913
    WORD version, offset;
    LPCSTR p = (LPCSTR)template;

    version = GET_WORD(p);
    p += sizeof(WORD);
3914
    TRACE("%p, ver %d\n", template, version );
Alexandre Julliard's avatar
Alexandre Julliard committed
3915
    switch (version)
3916
    {
3917
      case 0: /* standard format is version of 0 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3918 3919
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
3920
	if (!(hMenu = CreateMenu())) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3921 3922
	if (!MENU_ParseResource( p, hMenu, TRUE ))
	  {
3923
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3924 3925 3926
	    return 0;
	  }
	return hMenu;
3927
      case 1: /* extended format is version of 1 */
Alexandre Julliard's avatar
Alexandre Julliard committed
3928 3929
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
3930
	if (!(hMenu = CreateMenu())) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3931 3932
	if (!MENUEX_ParseResource( p, hMenu))
	  {
3933
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3934 3935 3936 3937
	    return 0;
	  }
	return hMenu;
      default:
3938
        ERR("version %d not supported.\n", version);
Alexandre Julliard's avatar
Alexandre Julliard committed
3939
        return 0;
3940
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3941 3942 3943 3944
}


/**********************************************************************
3945
 *	    LoadMenuIndirectA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3946
 */
3947
HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
3948
{
3949
    return LoadMenuIndirectW( template );
Alexandre Julliard's avatar
Alexandre Julliard committed
3950 3951 3952
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3953
/**********************************************************************
3954
 *		IsMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3955
 */
3956
BOOL WINAPI IsMenu(HMENU hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
3957
{
3958 3959
    LPPOPUPMENU menu = MENU_GetMenu(hmenu);
    return menu != NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3960 3961
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3962
/**********************************************************************
3963
 *		GetMenuItemInfo_common
Alexandre Julliard's avatar
Alexandre Julliard committed
3964 3965
 */

3966
static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
3967
					LPMENUITEMINFOW lpmii, BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
3968
{
3969 3970
    MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);

3971
    debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
3972

Alexandre Julliard's avatar
Alexandre Julliard committed
3973 3974 3975 3976 3977 3978
    if (!menu)
	return FALSE;

    if (lpmii->fMask & MIIM_TYPE) {
	lpmii->fType = menu->fType;
	switch (MENU_ITEM_TYPE(menu->fType)) {
3979
	case MF_STRING:
3980
            break;  /* will be done below */
3981 3982 3983 3984 3985 3986
	case MF_OWNERDRAW:
	case MF_BITMAP:
	    lpmii->dwTypeData = menu->text;
	    /* fall through */
	default:
	    lpmii->cch = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3987 3988
	}
    }
3989

3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015
    /* copy the text string */
    if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
         (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
    {
        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 );
            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 */
        if(lpmii->dwTypeData && lpmii->cch)
        {
            if (lpmii->cch <= len) lpmii->cch--;
        }
        else /* return length of string */
            lpmii->cch = len;
Alexandre Julliard's avatar
Alexandre Julliard committed
4016
    }
4017 4018 4019 4020 4021 4022 4023

    if (lpmii->fMask & MIIM_FTYPE)
	lpmii->fType = menu->fType;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
4024 4025
    if (lpmii->fMask & MIIM_STATE)
	lpmii->fState = menu->fState;
Alexandre Julliard's avatar
Alexandre Julliard committed
4026

Alexandre Julliard's avatar
Alexandre Julliard committed
4027 4028
    if (lpmii->fMask & MIIM_ID)
	lpmii->wID = menu->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
4029

Alexandre Julliard's avatar
Alexandre Julliard committed
4030 4031
    if (lpmii->fMask & MIIM_SUBMENU)
	lpmii->hSubMenu = menu->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4032

Alexandre Julliard's avatar
Alexandre Julliard committed
4033 4034 4035
    if (lpmii->fMask & MIIM_CHECKMARKS) {
	lpmii->hbmpChecked = menu->hCheckBit;
	lpmii->hbmpUnchecked = menu->hUnCheckBit;
Alexandre Julliard's avatar
Alexandre Julliard committed
4036
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4037 4038
    if (lpmii->fMask & MIIM_DATA)
	lpmii->dwItemData = menu->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
4039 4040 4041 4042 4043

  return TRUE;
}

/**********************************************************************
4044
 *		GetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4045
 */
4046 4047
BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOA lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4048
{
4049
    return GetMenuItemInfo_common (hmenu, item, bypos,
4050
                                    (LPMENUITEMINFOW)lpmii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4051 4052 4053
}

/**********************************************************************
4054
 *		GetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4055
 */
4056 4057
BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOW lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4058
{
4059
    return GetMenuItemInfo_common (hmenu, item, bypos,
4060
                                     lpmii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4061 4062
}

4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086

/* set a menu item text from a ASCII or Unicode string */
inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
{
    if (!text)
    {
        menu->text = NULL;
        menu->fType |= MF_SEPARATOR;
    }
    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 );
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4087
/**********************************************************************
4088
 *		SetMenuItemInfo_common
Alexandre Julliard's avatar
Alexandre Julliard committed
4089 4090
 */

4091
static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4092
				       const MENUITEMINFOW *lpmii,
4093
				       BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
4094 4095 4096
{
    if (!menu) return FALSE;

4097 4098
    debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");

4099
    if (lpmii->fMask & MIIM_TYPE ) {
4100 4101
	/* Get rid of old string. */
	if (IS_STRING_ITEM(menu->fType) && menu->text) {
4102
	    HeapFree(GetProcessHeap(), 0, menu->text);
4103 4104
	    menu->text = NULL;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
4105

4106
	/* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4107 4108
	menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
	menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4109

Alexandre Julliard's avatar
Alexandre Julliard committed
4110
	menu->text = lpmii->dwTypeData;
4111

4112 4113
       if (IS_STRING_ITEM(menu->fType))
           set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4114 4115 4116 4117 4118
    }

    if (lpmii->fMask & MIIM_FTYPE ) {
	/* free the string when the type is changing */
	if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4119
	    HeapFree(GetProcessHeap(), 0, menu->text);
4120 4121 4122 4123
	    menu->text = NULL;
	}
	menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
	menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4124 4125
        if ( IS_STRING_ITEM(menu->fType) && !menu->text )
            menu->fType |= MF_SEPARATOR;
4126 4127 4128
    }

    if (lpmii->fMask & MIIM_STRING ) {
4129 4130 4131 4132
	if (IS_STRING_ITEM(menu->fType)) {
            /* free the string when used */
            if(menu->text)
                HeapFree(GetProcessHeap(), 0, menu->text);
4133
            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
Alexandre Julliard's avatar
Alexandre Julliard committed
4134 4135
	}
    }
4136

Alexandre Julliard's avatar
Alexandre Julliard committed
4137
    if (lpmii->fMask & MIIM_STATE)
4138
    {
4139
	/* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
Alexandre Julliard's avatar
Alexandre Julliard committed
4140
	menu->fState = lpmii->fState;
4141
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4142 4143 4144 4145

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

Alexandre Julliard's avatar
Alexandre Julliard committed
4146
    if (lpmii->fMask & MIIM_SUBMENU) {
Alexandre Julliard's avatar
Alexandre Julliard committed
4147
	menu->hSubMenu = lpmii->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4148
	if (menu->hSubMenu) {
4149
	    POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4150
	    if (subMenu) {
Alexandre Julliard's avatar
Alexandre Julliard committed
4151 4152 4153 4154 4155 4156 4157 4158 4159 4160
		subMenu->wFlags |= MF_POPUP;
		menu->fType |= MF_POPUP;
	    }
	    else
		/* FIXME: Return an error ? */
		menu->fType &= ~MF_POPUP;
	}
	else
	    menu->fType &= ~MF_POPUP;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4161 4162 4163

    if (lpmii->fMask & MIIM_CHECKMARKS)
    {
4164 4165 4166
        if (lpmii->fType & MFT_RADIOCHECK)
            menu->fType |= MFT_RADIOCHECK;

Alexandre Julliard's avatar
Alexandre Julliard committed
4167 4168 4169 4170 4171 4172
	menu->hCheckBit = lpmii->hbmpChecked;
	menu->hUnCheckBit = lpmii->hbmpUnchecked;
    }
    if (lpmii->fMask & MIIM_DATA)
	menu->dwItemData = lpmii->dwItemData;

4173
    debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
4174 4175
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4176 4177

/**********************************************************************
4178
 *		SetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4179
 */
4180
BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4181
                                 const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4182
{
4183
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4184
				    (const MENUITEMINFOW *)lpmii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4185 4186 4187
}

/**********************************************************************
4188
 *		SetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4189
 */
4190 4191
BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4192
{
4193
    return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4194
				    lpmii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4195 4196 4197
}

/**********************************************************************
4198
 *		SetMenuDefaultItem    (USER32.@)
4199
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4200
 */
4201
BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
4202
{
4203 4204 4205
	UINT i;
	POPUPMENU *menu;
	MENUITEM *item;
4206

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

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

4211 4212 4213 4214 4215 4216
	/* reset all default-item flags */
	item = menu->items;
	for (i = 0; i < menu->nItems; i++, item++)
	{
	    item->fState &= ~MFS_DEFAULT;
	}
4217

4218 4219 4220 4221 4222
	/* no default item */
	if ( -1 == uItem)
	{
	    return TRUE;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
4223

4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240
	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;
		}
	    }
4241

4242 4243
	}
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4244 4245
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4246
/**********************************************************************
4247
 *		GetMenuDefaultItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4248
 */
4249
UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
4250
{
4251 4252 4253
	POPUPMENU *menu;
	MENUITEM * item;
	UINT i = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4254

4255
	TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4256

4257
	if (!(menu = MENU_GetMenu(hmenu))) return -1;
4258 4259 4260

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

4262 4263
	/* empty menu */
	if (! item) return -1;
4264

4265 4266 4267 4268 4269
	while ( !( item->fState & MFS_DEFAULT ) )
	{
	    i++; item++;
	    if  (i >= menu->nItems ) return -1;
	}
4270

4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283
	/* 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
4284 4285 4286

}

Alexandre Julliard's avatar
Alexandre Julliard committed
4287 4288

/**********************************************************************
4289
 *		InsertMenuItemA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4290
 */
4291 4292
BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4293
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4294
    MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4295
    return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4296 4297 4298 4299
}


/**********************************************************************
4300
 *		InsertMenuItemW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4301
 */
4302 4303
BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4304
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4305
    MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4306
    return SetMenuItemInfo_common(item, lpmii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4307
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4308 4309

/**********************************************************************
4310
 *		CheckMenuRadioItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4311 4312
 */

4313 4314 4315
BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
				   UINT first, UINT last, UINT check,
				   UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
4316 4317
{
     MENUITEM *mifirst, *milast, *micheck;
4318
     HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4319

4320
     TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
Alexandre Julliard's avatar
Alexandre Julliard committed
4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348

     mifirst = MENU_FindItem (&mfirst, &first, bypos);
     milast = MENU_FindItem (&mlast, &last, bypos);
     micheck = MENU_FindItem (&mcheck, &check, bypos);

     if (mifirst == NULL || milast == NULL || micheck == NULL ||
	 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
	 micheck > milast || micheck < mifirst)
	  return FALSE;

     while (mifirst <= milast)
     {
	  if (mifirst == micheck)
	  {
	       mifirst->fType |= MFT_RADIOCHECK;
	       mifirst->fState |= MFS_CHECKED;
	  } else {
	       mifirst->fType &= ~MFT_RADIOCHECK;
	       mifirst->fState &= ~MFS_CHECKED;
	  }
	  mifirst++;
     }

     return TRUE;
}


/**********************************************************************
4349
 *		GetMenuItemRect    (USER32.@)
4350
 *
4351 4352
 *      ATTENTION: Here, the returned values in rect are the screen
 *                 coordinates of the item just like if the menu was
4353
 *                 always on the upper left side of the application.
4354
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4355
 */
4356 4357
BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
				 LPRECT rect)
Alexandre Julliard's avatar
Alexandre Julliard committed
4358
{
4359
     POPUPMENU *itemMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4360
     MENUITEM *item;
4361
     HWND referenceHwnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
4362

4363
     TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
Alexandre Julliard's avatar
Alexandre Julliard committed
4364 4365

     item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4366 4367 4368 4369
     referenceHwnd = hwnd;

     if(!hwnd)
     {
4370
	 itemMenu = MENU_GetMenu(hMenu);
4371
	 if (itemMenu == NULL)
4372
	     return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4373

4374
	 if(itemMenu->hWnd == 0)
4375 4376 4377 4378
	     return FALSE;
	 referenceHwnd = itemMenu->hWnd;
     }

4379
     if ((rect == NULL) || (item == NULL))
4380
	 return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4381 4382 4383

     *rect = item->rect;

4384
     MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4385

Alexandre Julliard's avatar
Alexandre Julliard committed
4386 4387 4388
     return TRUE;
}

4389

4390
/**********************************************************************
4391
 *		SetMenuInfo    (USER32.@)
4392 4393 4394 4395 4396 4397 4398 4399 4400
 *
 * FIXME
 *	MIM_APPLYTOSUBMENUS
 *	actually use the items to draw the menu
 */
BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
{
    POPUPMENU *menu;

4401
    TRACE("(%p %p)\n", hMenu, lpmi);
4402

4403
    if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426
    {

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

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

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

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

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

	return TRUE;
    }
    return FALSE;
}

/**********************************************************************
4427
 *		GetMenuInfo    (USER32.@)
4428 4429 4430 4431 4432 4433 4434 4435
 *
 *  NOTES
 *	win98/NT5.0
 *
 */
BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
{   POPUPMENU *menu;

4436
    TRACE("(%p %p)\n", hMenu, lpmi);
4437

4438
    if (lpmi && (menu = MENU_GetMenu(hMenu)))
4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460
    {

	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;
    }
    return FALSE;
}

4461 4462

/**********************************************************************
4463
 *         SetMenuContextHelpId    (USER32.@)
4464
 */
4465
BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4466
{
4467 4468
    LPPOPUPMENU menu;

4469
    TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4470

4471
    if ((menu = MENU_GetMenu(hMenu)))
4472 4473 4474 4475 4476
    {
	menu->dwContextHelpID = dwContextHelpID;
	return TRUE;
    }
    return FALSE;
4477 4478
}

4479

4480
/**********************************************************************
4481
 *         GetMenuContextHelpId    (USER32.@)
4482
 */
4483
DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4484
{
4485 4486
    LPPOPUPMENU menu;

4487
    TRACE("(%p)\n", hMenu);
4488

4489
    if ((menu = MENU_GetMenu(hMenu)))
4490 4491 4492 4493
    {
	return menu->dwContextHelpID;
    }
    return 0;
4494
}
4495 4496

/**********************************************************************
4497
 *         MenuItemFromPoint    (USER32.@)
4498 4499 4500
 */
UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
{
4501 4502 4503 4504 4505 4506 4507 4508
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT pos;
    MENUITEM *item;

    /*FIXME: Do we have to handle hWnd here? */
    item = MENU_FindItemByCoords(menu, ptScreen, &pos);

    return pos;
4509
}
4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564


/**********************************************************************
 *           translate_accelerator
 */
static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                   BYTE fVirt, WORD key, WORD cmd )
{
    UINT mesg = 0;

    if (wParam != key) return FALSE;

    if (message == WM_CHAR)
    {
        if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
        {
            TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
            goto found;
        }
    }
    else
    {
        if(fVirt & FVIRTKEY)
        {
            INT mask = 0;
            TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
                          wParam, 0xff & HIWORD(lParam));
            if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
            if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
            if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
            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 */
                    TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
                    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;

4565
        hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4566
        hSysMenu = get_win_sys_menu( hWnd );
4567 4568 4569 4570 4571 4572 4573

        /* 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))
        {
4574 4575 4576 4577 4578
            if (GetCapture())
                mesg = 2;
            if (!IsWindowEnabled(hWnd))
                mesg = 3;
            else
4579
            {
4580 4581 4582 4583 4584 4585 4586 4587
                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);
4588 4589 4590 4591 4592 4593 4594 4595
            }
        }
        else /* 2. in the window's menu */
        {
            hSubMenu = hMenu;
            nPos = cmd;
            if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
            {
4596 4597 4598 4599 4600
                if (GetCapture())
                    mesg = 2;
                if (!IsWindowEnabled(hWnd))
                    mesg = 3;
                else
4601
                {
4602 4603 4604 4605 4606 4607 4608 4609
                    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);
4610 4611 4612 4613
                }
            }
        }

4614
        if (mesg == 0)
4615
        {
4616
            if (uSysStat != (UINT)-1)
4617
            {
4618 4619
                if (uSysStat & (MF_DISABLED|MF_GRAYED))
                    mesg=4;
4620
                else
4621 4622 4623 4624 4625
                    mesg=WM_SYSCOMMAND;
            }
            else
            {
                if (uStat != (UINT)-1)
4626
                {
4627 4628
                    if (IsIconic(hWnd))
                        mesg=5;
4629
                    else
4630 4631 4632 4633 4634 4635
                    {
                        if (uStat & (MF_DISABLED|MF_GRAYED))
                            mesg=6;
                        else
                            mesg=WM_COMMAND;
                    }
4636
                }
4637 4638
                else
                    mesg=WM_COMMAND;
4639 4640 4641 4642 4643 4644 4645
            }
        }
    }

    if( mesg==WM_COMMAND )
    {
        TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4646
        SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4647 4648 4649 4650
    }
    else if( mesg==WM_SYSCOMMAND )
    {
        TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4651
        SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4652 4653 4654
    }
    else
    {
4655
        /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
4656 4657 4658
         *   #0: unknown (please report!)
         *   #1: for WM_KEYUP,WM_SYSKEYUP
         *   #2: mouse is captured
4659
         *   #3: window is disabled
4660 4661 4662 4663 4664 4665
         *   #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)
4666
            ERR_(accel)(" unknown reason - please report!\n");
4667 4668 4669 4670 4671
    }
    return TRUE;
}

/**********************************************************************
4672
 *      TranslateAccelerator      (USER32.@)
4673
 *      TranslateAcceleratorA     (USER32.@)
4674
 *      TranslateAcceleratorW     (USER32.@)
4675
 */
4676
INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4677 4678 4679 4680 4681 4682 4683 4684 4685 4686
{
    /* YES, Accel16! */
    LPACCEL16 lpAccelTbl;
    int i;

    if (msg == NULL)
    {
        WARN_(accel)("msg null; should hang here to be win compatible\n");
        return 0;
    }
4687
    if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4688
    {
4689
        WARN_(accel)("invalid accel handle=%p\n", hAccel);
4690 4691 4692 4693 4694 4695 4696 4697
        return 0;
    }
    if ((msg->message != WM_KEYDOWN &&
         msg->message != WM_KEYUP &&
         msg->message != WM_SYSKEYDOWN &&
         msg->message != WM_SYSKEYUP &&
         msg->message != WM_CHAR)) return 0;

4698 4699
    TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
                  "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711
                  hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);

    i = 0;
    do
    {
        if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
                                   lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
            return 1;
    } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
    WARN_(accel)("couldn't translate accelerator key\n");
    return 0;
}