Prevent crash when no URL is specified.
[wine] / dlls / user / menu.c
1 /*
2  * Menu functions
3  *
4  * Copyright 1993 Martin Ayotte
5  * Copyright 1994 Alexandre Julliard
6  * Copyright 1997 Morten Welinder
7  * Copyright 2005 Maxime Bellengé
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 /*
25  * Note: the style MF_MOUSESELECT is used to mark popup items that
26  * have been selected, i.e. their popup menu is currently displayed.
27  * This is probably not the meaning this style has in MS-Windows.
28  *
29  * TODO:
30  *    implements styles :
31  *        - MNS_AUTODISMISS
32  *        - MNS_DRAGDROP
33  *        - MNS_MODELESS
34  *        - MNS_NOCHECK
35  *        - MNS_NOTIFYBYPOS
36  */
37
38 #include "config.h"
39 #include "wine/port.h"
40
41 #include <stdarg.h>
42 #include <string.h>
43
44 #include "windef.h"
45 #include "winbase.h"
46 #include "wingdi.h"
47 #include "winnls.h"
48 #include "wine/winbase16.h"
49 #include "wine/winuser16.h"
50 #include "wownt32.h"
51 #include "wine/server.h"
52 #include "wine/unicode.h"
53 #include "win.h"
54 #include "controls.h"
55 #include "user_private.h"
56 #include "wine/debug.h"
57
58 WINE_DEFAULT_DEBUG_CHANNEL(menu);
59 WINE_DECLARE_DEBUG_CHANNEL(accel);
60
61 /* internal popup menu window messages */
62
63 #define MM_SETMENUHANDLE        (WM_USER + 0)
64 #define MM_GETMENUHANDLE        (WM_USER + 1)
65
66 /* Menu item structure */
67 typedef struct {
68     /* ----------- MENUITEMINFO Stuff ----------- */
69     UINT fType;                 /* Item type. */
70     UINT fState;                /* Item state.  */
71     UINT_PTR wID;               /* Item id.  */
72     HMENU hSubMenu;             /* Pop-up menu.  */
73     HBITMAP hCheckBit;          /* Bitmap when checked.  */
74     HBITMAP hUnCheckBit;        /* Bitmap when unchecked.  */
75     LPWSTR text;                        /* Item text or bitmap handle.  */
76     DWORD dwItemData;           /* Application defined.  */
77     DWORD dwTypeData;           /* depends on fMask */
78     HBITMAP hbmpItem;           /* bitmap in win98 style menus */
79     /* ----------- Wine stuff ----------- */
80     RECT      rect;             /* Item area (relative to menu window) */
81     UINT      xTab;             /* X position of text after Tab */
82 } MENUITEM;
83
84 /* Popup menu structure */
85 typedef struct {
86     WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
87     WORD        wMagic;       /* Magic number */
88     WORD        Width;        /* Width of the whole menu */
89     WORD        Height;       /* Height of the whole menu */
90     UINT        nItems;       /* Number of items in the menu */
91     HWND        hWnd;         /* Window containing the menu */
92     MENUITEM    *items;       /* Array of menu items */
93     UINT        FocusedItem;  /* Currently focused item */
94     HWND        hwndOwner;    /* window receiving the messages for ownerdraw */
95     BOOL        bTimeToHide;  /* Request hiding when receiving a second click in the top-level menu item */
96     /* ------------ MENUINFO members ------ */
97     DWORD       dwStyle;        /* Extended mennu style */
98     UINT        cyMax;          /* max hight of the whole menu, 0 is screen hight */
99     HBRUSH      hbrBack;        /* brush for menu background */
100     DWORD       dwContextHelpID;
101     DWORD       dwMenuData;     /* application defined value */
102     HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
103     SIZE        maxBmpSize;     /* Maximum size of the bitmap items in MIIM_BITMAP state */
104 } POPUPMENU, *LPPOPUPMENU;
105
106 /* internal flags for menu tracking */
107
108 #define TF_ENDMENU              0x0001
109 #define TF_SUSPENDPOPUP         0x0002
110 #define TF_SKIPREMOVE           0x0004
111
112 typedef struct
113 {
114     UINT        trackFlags;
115     HMENU       hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
116     HMENU       hTopMenu;     /* initial menu */
117     HWND        hOwnerWnd;    /* where notifications are sent */
118     POINT       pt;
119 } MTRACKER;
120
121 #define MENU_MAGIC   0x554d  /* 'MU' */
122
123 #define ITEM_PREV               -1
124 #define ITEM_NEXT                1
125
126   /* Internal MENU_TrackMenu() flags */
127 #define TPM_INTERNAL            0xF0000000
128 #define TPM_ENTERIDLEEX         0x80000000              /* set owner window for WM_ENTERIDLE */
129 #define TPM_BUTTONDOWN          0x40000000              /* menu was clicked before tracking */
130 #define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
131
132   /* popup menu shade thickness */
133 #define POPUP_XSHADE            4
134 #define POPUP_YSHADE            4
135
136   /* Space between 2 menu bar items */
137 #define MENU_BAR_ITEMS_SPACE 12
138
139   /* Minimum width of a tab character */
140 #define MENU_TAB_SPACE 8
141
142   /* Height of a separator item */
143 #define SEPARATOR_HEIGHT 5
144
145   /* (other menu->FocusedItem values give the position of the focused item) */
146 #define NO_SELECTED_ITEM  0xffff
147
148 #define MENU_ITEM_TYPE(flags) \
149   ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
150
151 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
152 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
153 #define IS_MAGIC_ITEM(text)   (LOWORD((int)text)<12)
154
155 #define IS_SYSTEM_MENU(menu)  \
156         (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
157
158 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
159                    MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
160                    MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
161                    MF_POPUP | MF_SYSMENU | MF_HELP)
162 #define STATE_MASK (~TYPE_MASK)
163
164   /* Dimension of the menu bitmaps */
165 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
166
167 static HBITMAP hStdMnArrow = 0;
168 static HBITMAP hBmpSysMenu = 0;
169
170 static HBRUSH   hShadeBrush = 0;
171 static HFONT    hMenuFont = 0;
172 static HFONT    hMenuFontBold = 0;
173
174 static HMENU MENU_DefSysPopup = 0;  /* Default system menu popup */
175
176 /* Use global popup window because there's no way 2 menus can
177  * be tracked at the same time.  */
178 static HWND top_popup;
179
180   /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
182
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
184
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
186
187 /*********************************************************************
188  * menu class descriptor
189  */
190 const struct builtin_class_descr MENU_builtin_class =
191 {
192     POPUPMENU_CLASS_ATOMA,         /* name */
193     CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
194     NULL,                          /* procA (winproc is Unicode only) */
195     PopupMenuWndProc,              /* procW */
196     sizeof(HMENU),                 /* extra */
197     IDC_ARROW,                     /* cursor */
198     (HBRUSH)(COLOR_MENU+1)         /* brush */
199 };
200
201
202 /***********************************************************************
203  *           debug_print_menuitem
204  *
205  * Print a menuitem in readable form.
206  */
207
208 #define debug_print_menuitem(pre, mp, post) \
209   if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
210
211 #define MENUOUT(text) \
212   DPRINTF("%s%s", (count++ ? "," : ""), (text))
213
214 #define MENUFLAG(bit,text) \
215   do { \
216     if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217   } while (0)
218
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
220                                     const char *postfix)
221 {
222     TRACE("%s ", prefix);
223     if (mp) {
224         UINT flags = mp->fType;
225         int type = MENU_ITEM_TYPE(flags);
226         DPRINTF( "{ ID=0x%x", mp->wID);
227         if (flags & MF_POPUP)
228             DPRINTF( ", Sub=%p", mp->hSubMenu);
229         if (flags) {
230             int count = 0;
231             DPRINTF( ", Type=");
232             if (type == MFT_STRING)
233                 /* Nothing */ ;
234             else if (type == MFT_SEPARATOR)
235                 MENUOUT("sep");
236             else if (type == MFT_OWNERDRAW)
237                 MENUOUT("own");
238             else if (type == MFT_BITMAP)
239                 MENUOUT("bit");
240             else
241                 MENUOUT("???");
242             flags -= type;
243
244             MENUFLAG(MF_POPUP, "pop");
245             MENUFLAG(MFT_MENUBARBREAK, "barbrk");
246             MENUFLAG(MFT_MENUBREAK, "brk");
247             MENUFLAG(MFT_RADIOCHECK, "radio");
248             MENUFLAG(MFT_RIGHTORDER, "rorder");
249             MENUFLAG(MF_SYSMENU, "sys");
250             MENUFLAG(MFT_RIGHTJUSTIFY, "right");        /* same as MF_HELP */
251
252             if (flags)
253                 DPRINTF( "+0x%x", flags);
254         }
255         flags = mp->fState;
256         if (flags) {
257             int count = 0;
258             DPRINTF( ", State=");
259             MENUFLAG(MFS_GRAYED, "grey");
260             MENUFLAG(MFS_DEFAULT, "default");
261             MENUFLAG(MFS_DISABLED, "dis");
262             MENUFLAG(MFS_CHECKED, "check");
263             MENUFLAG(MFS_HILITE, "hi");
264             MENUFLAG(MF_USECHECKBITMAPS, "usebit");
265             MENUFLAG(MF_MOUSESELECT, "mouse");
266             if (flags)
267                 DPRINTF( "+0x%x", flags);
268         }
269         if (mp->hCheckBit)
270             DPRINTF( ", Chk=%p", mp->hCheckBit);
271         if (mp->hUnCheckBit)
272             DPRINTF( ", Unc=%p", mp->hUnCheckBit);
273
274         if (type == MFT_STRING) {
275             if (mp->text)
276                 DPRINTF( ", Text=%s", debugstr_w(mp->text));
277             else
278                 DPRINTF( ", Text=Null");
279         } else if (mp->text == NULL)
280             /* Nothing */ ;
281         else
282             DPRINTF( ", Text=%p", mp->text);
283         if (mp->dwItemData)
284             DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
285         DPRINTF( " }");
286     } else {
287         DPRINTF( "NULL");
288     }
289
290     DPRINTF(" %s\n", postfix);
291 }
292
293 #undef MENUOUT
294 #undef MENUFLAG
295
296
297 /***********************************************************************
298  *           MENU_GetMenu
299  *
300  * Validate the given menu handle and returns the menu structure pointer.
301  */
302 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
303 {
304     POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
305     if (!menu || menu->wMagic != MENU_MAGIC)
306     {
307         WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
308         menu = NULL;
309     }
310     return menu;
311 }
312
313 /***********************************************************************
314  *           get_win_sys_menu
315  *
316  * Get the system menu of a window
317  */
318 static HMENU get_win_sys_menu( HWND hwnd )
319 {
320     HMENU ret = 0;
321     WND *win = WIN_GetPtr( hwnd );
322     if (win && win != WND_OTHER_PROCESS)
323     {
324         ret = win->hSysMenu;
325         WIN_ReleasePtr( win );
326     }
327     return ret;
328 }
329
330 /***********************************************************************
331  *           MENU_CopySysPopup
332  *
333  * Return the default system menu.
334  */
335 static HMENU MENU_CopySysPopup(void)
336 {
337     static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
338     HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
339
340     if( hMenu ) {
341         POPUPMENU* menu = MENU_GetMenu(hMenu);
342         menu->wFlags |= MF_SYSMENU | MF_POPUP;
343         SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
344     }
345     else
346         ERR("Unable to load default system menu\n" );
347
348     TRACE("returning %p.\n", hMenu );
349
350     return hMenu;
351 }
352
353
354 /**********************************************************************
355  *           MENU_GetSysMenu
356  *
357  * Create a copy of the system menu. System menu in Windows is
358  * a special menu bar with the single entry - system menu popup.
359  * This popup is presented to the outside world as a "system menu".
360  * However, the real system menu handle is sometimes seen in the
361  * WM_MENUSELECT parameters (and Word 6 likes it this way).
362  */
363 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
364 {
365     HMENU hMenu;
366
367     TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
368     if ((hMenu = CreateMenu()))
369     {
370         POPUPMENU *menu = MENU_GetMenu(hMenu);
371         menu->wFlags = MF_SYSMENU;
372         menu->hWnd = WIN_GetFullHandle( hWnd );
373         TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
374
375         if (hPopupMenu == (HMENU)(-1))
376             hPopupMenu = MENU_CopySysPopup();
377         else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
378
379         if (hPopupMenu)
380         {
381             InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
382                          (UINT_PTR)hPopupMenu, NULL );
383
384             menu->items[0].fType = MF_SYSMENU | MF_POPUP;
385             menu->items[0].fState = 0;
386             if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
387
388             TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
389             return hMenu;
390         }
391         DestroyMenu( hMenu );
392     }
393     ERR("failed to load system menu!\n");
394     return 0;
395 }
396
397
398 /***********************************************************************
399  *           MENU_Init
400  *
401  * Menus initialisation.
402  */
403 BOOL MENU_Init(void)
404 {
405     HBITMAP hBitmap;
406     NONCLIENTMETRICSW ncm;
407
408     static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
409                                             0x55, 0, 0xAA, 0,
410                                             0x55, 0, 0xAA, 0,
411                                             0x55, 0, 0xAA, 0 };
412
413     /* Load menu bitmaps */
414     hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
415     /* Load system buttons bitmaps */
416     hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
417
418     if (hStdMnArrow)
419     {
420         BITMAP bm;
421         GetObjectW( hStdMnArrow, sizeof(bm), &bm );
422         arrow_bitmap_width = bm.bmWidth;
423         arrow_bitmap_height = bm.bmHeight;
424     } else
425         return FALSE;
426
427     if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
428         return FALSE;
429
430     if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
431         return FALSE;
432
433     DeleteObject( hBitmap );
434     if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
435         return FALSE;
436
437     ncm.cbSize = sizeof(NONCLIENTMETRICSW);
438     if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
439         return FALSE;
440
441     if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
442         return FALSE;
443
444     ncm.lfMenuFont.lfWeight += 300;
445     if ( ncm.lfMenuFont.lfWeight > 1000)
446         ncm.lfMenuFont.lfWeight = 1000;
447
448     if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
449         return FALSE;
450
451     return TRUE;
452 }
453
454 /***********************************************************************
455  *           MENU_InitSysMenuPopup
456  *
457  * Grey the appropriate items in System menu.
458  */
459 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
460 {
461     BOOL gray;
462
463     gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
464     EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
465     gray = ((style & WS_MAXIMIZE) != 0);
466     EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
467     gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
468     EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
469     gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
470     EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
471     gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
472     EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
473     gray = (clsStyle & CS_NOCLOSE) != 0;
474
475     /* The menu item must keep its state if it's disabled */
476     if(gray)
477         EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
478 }
479
480
481 /******************************************************************************
482  *
483  *   UINT  MENU_GetStartOfNextColumn(
484  *     HMENU  hMenu )
485  *
486  *****************************************************************************/
487
488 static UINT  MENU_GetStartOfNextColumn(
489     HMENU  hMenu )
490 {
491     POPUPMENU *menu = MENU_GetMenu(hMenu);
492     UINT i;
493
494     if(!menu)
495         return NO_SELECTED_ITEM;
496
497     i = menu->FocusedItem + 1;
498     if( i == NO_SELECTED_ITEM )
499         return i;
500
501     for( ; i < menu->nItems; ++i ) {
502         if (menu->items[i].fType & MF_MENUBARBREAK)
503             return i;
504     }
505
506     return NO_SELECTED_ITEM;
507 }
508
509
510 /******************************************************************************
511  *
512  *   UINT  MENU_GetStartOfPrevColumn(
513  *     HMENU  hMenu )
514  *
515  *****************************************************************************/
516
517 static UINT  MENU_GetStartOfPrevColumn(
518     HMENU  hMenu )
519 {
520     POPUPMENU *menu = MENU_GetMenu(hMenu);
521     UINT  i;
522
523     if( !menu )
524         return NO_SELECTED_ITEM;
525
526     if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
527         return NO_SELECTED_ITEM;
528
529     /* Find the start of the column */
530
531     for(i = menu->FocusedItem; i != 0 &&
532          !(menu->items[i].fType & MF_MENUBARBREAK);
533         --i); /* empty */
534
535     if(i == 0)
536         return NO_SELECTED_ITEM;
537
538     for(--i; i != 0; --i) {
539         if (menu->items[i].fType & MF_MENUBARBREAK)
540             break;
541     }
542
543     TRACE("ret %d.\n", i );
544
545     return i;
546 }
547
548
549
550 /***********************************************************************
551  *           MENU_FindItem
552  *
553  * Find a menu item. Return a pointer on the item, and modifies *hmenu
554  * in case the item was in a sub-menu.
555  */
556 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
557 {
558     POPUPMENU *menu;
559     UINT i;
560
561     if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
562     if (wFlags & MF_BYPOSITION)
563     {
564         if (*nPos >= menu->nItems) return NULL;
565         return &menu->items[*nPos];
566     }
567     else
568     {
569         MENUITEM *item = menu->items;
570         for (i = 0; i < menu->nItems; i++, item++)
571         {
572             if (item->wID == *nPos)
573             {
574                 *nPos = i;
575                 return item;
576             }
577             else if (item->fType & MF_POPUP)
578             {
579                 HMENU hsubmenu = item->hSubMenu;
580                 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
581                 if (subitem)
582                 {
583                     *hmenu = hsubmenu;
584                     return subitem;
585                 }
586             }
587         }
588     }
589     return NULL;
590 }
591
592 /***********************************************************************
593  *           MENU_FindSubMenu
594  *
595  * Find a Sub menu. Return the position of the submenu, and modifies
596  * *hmenu in case it is found in another sub-menu.
597  * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
598  */
599 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
600 {
601     POPUPMENU *menu;
602     UINT i;
603     MENUITEM *item;
604     if (((*hmenu)==(HMENU)0xffff) ||
605             (!(menu = MENU_GetMenu(*hmenu))))
606         return NO_SELECTED_ITEM;
607     item = menu->items;
608     for (i = 0; i < menu->nItems; i++, item++) {
609         if(!(item->fType & MF_POPUP)) continue;
610         if (item->hSubMenu == hSubTarget) {
611             return i;
612         }
613         else  {
614             HMENU hsubmenu = item->hSubMenu;
615             UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
616             if (pos != NO_SELECTED_ITEM) {
617                 *hmenu = hsubmenu;
618                 return pos;
619             }
620         }
621     }
622     return NO_SELECTED_ITEM;
623 }
624
625 /***********************************************************************
626  *           MENU_FreeItemData
627  */
628 static void MENU_FreeItemData( MENUITEM* item )
629 {
630     /* delete text */
631     if (IS_STRING_ITEM(item->fType) && item->text)
632         HeapFree( GetProcessHeap(), 0, item->text );
633 }
634
635 /***********************************************************************
636  *           MENU_FindItemByCoords
637  *
638  * Find the item at the specified coordinates (screen coords). Does
639  * not work for child windows and therefore should not be called for
640  * an arbitrary system menu.
641  */
642 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
643                                         POINT pt, UINT *pos )
644 {
645     MENUITEM *item;
646     UINT i;
647     RECT wrect;
648
649     if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
650     pt.x -= wrect.left;pt.y -= wrect.top;
651     item = menu->items;
652     for (i = 0; i < menu->nItems; i++, item++)
653     {
654         if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
655             (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
656         {
657             if (pos) *pos = i;
658             return item;
659         }
660     }
661     return NULL;
662 }
663
664
665 /***********************************************************************
666  *           MENU_FindItemByKey
667  *
668  * Find the menu item selected by a key press.
669  * Return item id, -1 if none, -2 if we should close the menu.
670  */
671 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
672                                 WCHAR key, BOOL forceMenuChar )
673 {
674     TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
675
676     if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
677
678     if (hmenu)
679     {
680         POPUPMENU *menu = MENU_GetMenu( hmenu );
681         MENUITEM *item = menu->items;
682         LRESULT menuchar;
683
684         if( !forceMenuChar )
685         {
686              UINT i;
687
688              for (i = 0; i < menu->nItems; i++, item++)
689              {
690                 if (IS_STRING_ITEM(item->fType) && item->text)
691                 {
692                     WCHAR *p = item->text - 2;
693                     do
694                     {
695                         p = strchrW (p + 2, '&');
696                     }
697                     while (p != NULL && p [1] == '&');
698                     if (p && (toupperW(p[1]) == toupperW(key))) return i;
699                 }
700              }
701         }
702         menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
703                                  MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
704         if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
705         if (HIWORD(menuchar) == 1) return (UINT)(-2);
706     }
707     return (UINT)(-1);
708 }
709
710
711 /***********************************************************************
712  *           MENU_GetBitmapItemSize
713  *
714  * Get the size of a bitmap item.
715  */
716 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
717 {
718     BITMAP bm;
719     HBITMAP bmp = (HBITMAP)id;
720
721     size->cx = size->cy = 0;
722
723     /* check if there is a magic menu item associated with this item */
724     if (id && IS_MAGIC_ITEM( id ))
725     {
726         switch(LOWORD(id))
727         {
728         case (INT_PTR)HBMMENU_SYSTEM:
729             if (data)
730             {
731                 bmp = (HBITMAP)data;
732                 break;
733             }
734             /* fall through */
735         case (INT_PTR)HBMMENU_MBAR_RESTORE:
736         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
737         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
738         case (INT_PTR)HBMMENU_MBAR_CLOSE:
739         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
740             size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
741             size->cy = size->cx;
742             return;
743         case (INT_PTR)HBMMENU_CALLBACK:
744         case (INT_PTR)HBMMENU_POPUP_CLOSE:
745         case (INT_PTR)HBMMENU_POPUP_RESTORE:
746         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
747         case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
748         default:
749             FIXME("Magic 0x%08x not implemented\n", id);
750             return;
751         }
752     }
753     if (GetObjectW(bmp, sizeof(bm), &bm ))
754     {
755         size->cx = bm.bmWidth;
756         size->cy = bm.bmHeight;
757     }
758 }
759
760 /***********************************************************************
761  *           MENU_DrawBitmapItem
762  *
763  * Draw a bitmap item.
764  * drawhbmbitmap : True to draw the hbmbitmap(MIIM_BITMAP)/False to draw the MF_BITMAP
765  */
766 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar, BOOL drawhbmbitmap )
767 {
768     BITMAP bm;
769     DWORD rop;
770     HDC hdcMem;
771     HBITMAP bmp = (HBITMAP)lpitem->text;
772     int w = rect->right - rect->left;
773     int h = rect->bottom - rect->top;
774     int bmp_xoffset = 0;
775     int left, top;
776     HBITMAP hbmToDraw = (drawhbmbitmap)?lpitem->hbmpItem:(HBITMAP)lpitem->text;    
777
778     /* Check if there is a magic menu item associated with this item */
779     if (hbmToDraw && IS_MAGIC_ITEM(hbmToDraw))
780     {
781         UINT flags = 0;
782         RECT r;
783
784         switch(LOWORD(hbmToDraw))
785         {
786         case (INT_PTR)HBMMENU_SYSTEM:
787             if (lpitem->dwItemData)
788             {
789                 bmp = (HBITMAP)lpitem->dwItemData;
790                 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
791             }
792             else
793             {
794                 bmp = hBmpSysMenu;
795                 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
796                 /* only use right half of the bitmap */
797                 bmp_xoffset = bm.bmWidth / 2;
798                 bm.bmWidth -= bmp_xoffset;
799             }
800             goto got_bitmap;
801         case (INT_PTR)HBMMENU_MBAR_RESTORE:
802             flags = DFCS_CAPTIONRESTORE;
803             break;
804         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
805             flags = DFCS_CAPTIONMIN;
806             break;
807         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
808             flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
809             break;
810         case (INT_PTR)HBMMENU_MBAR_CLOSE:
811             flags = DFCS_CAPTIONCLOSE;
812             break;
813         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814             flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
815             break;
816         case (INT_PTR)HBMMENU_CALLBACK:
817         case (INT_PTR)HBMMENU_POPUP_CLOSE:
818         case (INT_PTR)HBMMENU_POPUP_RESTORE:
819         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820         case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821         default:
822             FIXME("Magic 0x%08x not implemented\n", LOWORD(hbmToDraw)); 
823             return;
824         }
825         r = *rect;
826         InflateRect( &r, -1, -1 );
827         if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
828         DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
829         return;
830     }
831
832     if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
833
834  got_bitmap:
835     hdcMem = CreateCompatibleDC( hdc );
836     SelectObject( hdcMem, bmp );
837
838     /* handle fontsize > bitmap_height */
839     top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
840     left=rect->left;
841     rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
842     if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
843         SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
844     BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
845     DeleteDC( hdcMem );
846 }
847
848
849 /***********************************************************************
850  *           MENU_CalcItemSize
851  *
852  * Calculate the size of the menu item and store it in lpitem->rect.
853  */
854 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
855                                INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
856 {
857     WCHAR *p;
858     UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
859
860     TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
861     debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
862                          (menuBar ? " (MenuBar)" : ""));
863
864     SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
865
866     if (lpitem->fType & MF_OWNERDRAW)
867     {
868         /*
869         ** Experimentation under Windows reveals that an owner-drawn
870         ** menu is expected to return the size of the content part of
871         ** the menu item, not including the checkmark nor the submenu
872         ** arrow.  Windows adds those values itself and returns the
873         ** enlarged rectangle on subsequent WM_DRAWITEM messages.
874         */
875         MEASUREITEMSTRUCT mis;
876         mis.CtlType    = ODT_MENU;
877         mis.CtlID      = 0;
878         mis.itemID     = lpitem->wID;
879         mis.itemData   = (DWORD)lpitem->dwItemData;
880         mis.itemHeight = 0;
881         mis.itemWidth  = 0;
882         SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
883         lpitem->rect.right  += mis.itemWidth;
884
885         if (menuBar)
886         {
887              lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
888
889
890              /* under at least win95 you seem to be given a standard
891                 height for the menu and the height value is ignored */
892              lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
893         }
894         else
895             lpitem->rect.bottom += mis.itemHeight;
896
897         TRACE("id=%04x size=%dx%d\n",
898                      lpitem->wID, mis.itemWidth, mis.itemHeight);
899         /* Fall through to get check/arrow width calculation. */
900     }
901
902     if (lpitem->fType & MF_SEPARATOR)
903     {
904         lpitem->rect.bottom += SEPARATOR_HEIGHT;
905         return;
906     }
907
908     if (!menuBar)
909     {
910         /* New style MIIM_BITMAP */
911         if (lpitem->hbmpItem) 
912         {
913             if (lpitem->hbmpItem == HBMMENU_CALLBACK)
914             {
915                 MEASUREITEMSTRUCT measItem;
916                 measItem.CtlType = ODT_MENU;
917                 measItem.CtlID = 0;
918                 measItem.itemID = lpitem->wID;
919                 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
920                 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
921                 measItem.itemData = lpitem->dwItemData;
922                 
923                 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
924             
925                 /* Keep the size of the bitmap in callback mode to be able to draw it correctly */
926                 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, measItem.itemWidth - (lpitem->rect.right - lpitem->rect.left));
927                 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, measItem.itemHeight - (lpitem->rect.bottom - lpitem->rect.top));
928                 lpitem->rect.right = lpitem->rect.left + measItem.itemWidth;
929             } else {
930                 SIZE size;
931                 MENU_GetBitmapItemSize((UINT)lpitem->hbmpItem, lpitem->dwItemData, &size);
932                 lppop->maxBmpSize.cx = max(lppop->maxBmpSize.cx, size.cx);
933                 lppop->maxBmpSize.cy = max(lppop->maxBmpSize.cy, size.cy);
934                 lpitem->rect.right  += size.cx;
935                 lpitem->rect.bottom += size.cy;
936             }
937             if (lppop->dwStyle & MNS_CHECKORBMP) 
938                 lpitem->rect.right += check_bitmap_width;
939             else
940                 lpitem->rect.right += 2 * check_bitmap_width;
941         } else
942             lpitem->rect.right += 2 * check_bitmap_width;
943         if (lpitem->fType & MF_POPUP)
944             lpitem->rect.right += arrow_bitmap_width;
945     }
946
947     if (lpitem->fType & MF_OWNERDRAW)
948         return;
949
950     if (IS_BITMAP_ITEM(lpitem->fType))
951     {
952         SIZE size;
953
954         MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
955         lpitem->rect.right  += size.cx;
956         lpitem->rect.bottom += size.cy;
957         /* Leave space for the sunken border */
958         lpitem->rect.right  += 2;
959         lpitem->rect.bottom += 2;
960     }
961
962
963     /* it must be a text item - unless it's the system menu */
964     if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
965     {   SIZE size;
966
967         GetTextExtentPoint32W(hdc, lpitem->text,  strlenW(lpitem->text), &size);
968
969         lpitem->rect.right  += size.cx;
970         lpitem->rect.bottom += max(max(size.cy, GetSystemMetrics(SM_CYMENU)-1), lppop->maxBmpSize.cy);
971         lpitem->xTab = 0;
972
973         if (menuBar)
974         {
975              lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
976         }
977         else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
978         {
979             /* Item contains a tab (only meaningful in popup menus) */
980             GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
981             lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
982             lpitem->rect.right += MENU_TAB_SPACE;
983         }
984         else
985         {
986             if (strchrW( lpitem->text, '\b' ))
987                 lpitem->rect.right += MENU_TAB_SPACE;
988             lpitem->xTab = lpitem->rect.right - check_bitmap_width
989                            - arrow_bitmap_width;
990         }
991     }
992     TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
993 }
994
995
996 /***********************************************************************
997  *           MENU_PopupMenuCalcSize
998  *
999  * Calculate the size of a popup menu.
1000  */
1001 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1002 {
1003     MENUITEM *lpitem;
1004     HDC hdc;
1005     int start, i;
1006     int orgX, orgY, maxX, maxTab, maxTabWidth;
1007
1008     lppop->Width = lppop->Height = 0;
1009     if (lppop->nItems == 0) return;
1010     hdc = GetDC( 0 );
1011
1012     SelectObject( hdc, hMenuFont);
1013
1014     start = 0;
1015     maxX = 2 + 1;
1016
1017     lppop->maxBmpSize.cx = 0;
1018     lppop->maxBmpSize.cy = 0;
1019
1020     while (start < lppop->nItems)
1021     {
1022         lpitem = &lppop->items[start];
1023         orgX = maxX;
1024         orgY = 3;
1025
1026         maxTab = maxTabWidth = 0;
1027           /* Parse items until column break or end of menu */
1028         for (i = start; i < lppop->nItems; i++, lpitem++)
1029         {
1030             if ((i != start) &&
1031                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1032
1033             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1034
1035             if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1036             maxX = max( maxX, lpitem->rect.right );
1037             orgY = lpitem->rect.bottom;
1038             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1039             {
1040                 maxTab = max( maxTab, lpitem->xTab );
1041                 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1042             }
1043         }
1044
1045           /* Finish the column (set all items to the largest width found) */
1046         maxX = max( maxX, maxTab + maxTabWidth );
1047         for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1048         {
1049             lpitem->rect.right = maxX;
1050             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1051                 lpitem->xTab = maxTab;
1052
1053         }
1054         lppop->Height = max( lppop->Height, orgY );
1055     }
1056
1057     lppop->Width  = maxX;
1058
1059     /* space for 3d border */
1060     lppop->Height += 2;
1061     lppop->Width += 2;
1062
1063     ReleaseDC( 0, hdc );
1064 }
1065
1066
1067 /***********************************************************************
1068  *           MENU_MenuBarCalcSize
1069  *
1070  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1071  * height is off by 1 pixel which causes lengthy window relocations when
1072  * active document window is maximized/restored.
1073  *
1074  * Calculate the size of the menu bar.
1075  */
1076 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1077                                   LPPOPUPMENU lppop, HWND hwndOwner )
1078 {
1079     MENUITEM *lpitem;
1080     int start, i, orgX, orgY, maxY, helpPos;
1081
1082     if ((lprect == NULL) || (lppop == NULL)) return;
1083     if (lppop->nItems == 0) return;
1084     TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1085           lprect->left, lprect->top, lprect->right, lprect->bottom);
1086     lppop->Width  = lprect->right - lprect->left;
1087     lppop->Height = 0;
1088     maxY = lprect->top+1;
1089     start = 0;
1090     helpPos = -1;
1091     lppop->maxBmpSize.cx = 0;
1092     lppop->maxBmpSize.cy = 0;
1093     while (start < lppop->nItems)
1094     {
1095         lpitem = &lppop->items[start];
1096         orgX = lprect->left;
1097         orgY = maxY;
1098
1099           /* Parse items until line break or end of menu */
1100         for (i = start; i < lppop->nItems; i++, lpitem++)
1101         {
1102             if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1103             if ((i != start) &&
1104                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1105
1106             TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1107                          orgX, orgY );
1108             debug_print_menuitem ("  item: ", lpitem, "");
1109             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1110
1111             if (lpitem->rect.right > lprect->right)
1112             {
1113                 if (i != start) break;
1114                 else lpitem->rect.right = lprect->right;
1115             }
1116             maxY = max( maxY, lpitem->rect.bottom );
1117             orgX = lpitem->rect.right;
1118         }
1119
1120           /* Finish the line (set all items to the largest height found) */
1121         while (start < i) lppop->items[start++].rect.bottom = maxY;
1122     }
1123
1124     lprect->bottom = maxY;
1125     lppop->Height = lprect->bottom - lprect->top;
1126
1127     /* Flush right all items between the MF_RIGHTJUSTIFY and */
1128     /* the last item (if several lines, only move the last line) */
1129     lpitem = &lppop->items[lppop->nItems-1];
1130     orgY = lpitem->rect.top;
1131     orgX = lprect->right;
1132     for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1133         if ( (helpPos==-1) || (helpPos>i) )
1134             break;                              /* done */
1135         if (lpitem->rect.top != orgY) break;    /* Other line */
1136         if (lpitem->rect.right >= orgX) break;  /* Too far right already */
1137         lpitem->rect.left += orgX - lpitem->rect.right;
1138         lpitem->rect.right = orgX;
1139         orgX = lpitem->rect.left;
1140     }
1141 }
1142
1143 /***********************************************************************
1144  *           MENU_DrawMenuItem
1145  *
1146  * Draw a single menu item.
1147  */
1148 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1149                                UINT height, BOOL menuBar, UINT odaction )
1150 {
1151     RECT rect;
1152
1153     debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1154
1155     if (lpitem->fType & MF_SYSMENU)
1156     {
1157         if( !IsIconic(hwnd) )
1158             NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1159         return;
1160     }
1161
1162       /* Setup colors */
1163
1164     if (lpitem->fState & MF_HILITE)
1165     {
1166         if(menuBar) {
1167             SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1168             SetBkColor(hdc, GetSysColor(COLOR_MENU));
1169         } else {
1170             if(lpitem->fState & MF_GRAYED)
1171                 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1172             else
1173                 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1174             SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1175         }
1176     }
1177     else
1178     {
1179         if (lpitem->fState & MF_GRAYED)
1180             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1181         else
1182             SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1183         SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1184     }
1185
1186     if (lpitem->fType & MF_OWNERDRAW)
1187     {
1188         /*
1189         ** Experimentation under Windows reveals that an owner-drawn
1190         ** menu is given the rectangle which includes the space it requested
1191         ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1192         ** and a popup-menu arrow.  This is the value of lpitem->rect.
1193         ** Windows will leave all drawing to the application except for
1194         ** the popup-menu arrow.  Windows always draws that itself, after
1195         ** the menu owner has finished drawing.
1196         */
1197         DRAWITEMSTRUCT dis;
1198
1199         dis.CtlType   = ODT_MENU;
1200         dis.CtlID     = 0;
1201         dis.itemID    = lpitem->wID;
1202         dis.itemData  = (DWORD)lpitem->dwItemData;
1203         dis.itemState = 0;
1204         if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1205         if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1206         if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
1207         dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1208         dis.hwndItem   = (HWND)hmenu;
1209         dis.hDC        = hdc;
1210         dis.rcItem     = lpitem->rect;
1211         TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1212               "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1213               dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1214               dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1215               dis.rcItem.bottom);
1216         SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1217         /* Fall through to draw popup-menu arrow */
1218     }
1219
1220     TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1221                                         lpitem->rect.right,lpitem->rect.bottom);
1222
1223     if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1224
1225     rect = lpitem->rect;
1226
1227     if (!(lpitem->fType & MF_OWNERDRAW))
1228     {
1229         if (lpitem->fState & MF_HILITE)
1230         {
1231              if(menuBar)
1232                 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1233              else
1234                 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1235         }
1236         else
1237             FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1238     }
1239
1240     SetBkMode( hdc, TRANSPARENT );
1241
1242     if (!(lpitem->fType & MF_OWNERDRAW))
1243     {
1244         /* vertical separator */
1245         if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1246         {
1247             RECT rc = rect;
1248             rc.top = 3;
1249             rc.bottom = height - 3;
1250             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1251         }
1252
1253         /* horizontal separator */
1254         if (lpitem->fType & MF_SEPARATOR)
1255         {
1256             RECT rc = rect;
1257             rc.left++;
1258             rc.right--;
1259             rc.top += SEPARATOR_HEIGHT / 2;
1260             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1261             return;
1262         }
1263     }
1264
1265         /* helper lines for debugging */
1266 /*      FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1267         SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1268         MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1269         LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1270 */
1271
1272     if (!menuBar)
1273     {
1274         HBITMAP bm;
1275         INT y = rect.top + rect.bottom;
1276         UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1277         UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1278
1279         if (!(lpitem->fType & MF_OWNERDRAW))
1280         {
1281             /* New style MIIM_BITMAP */
1282             if (lpitem->hbmpItem)
1283             {
1284                 POPUPMENU *menu = MENU_GetMenu(hmenu);
1285                 HBITMAP hbm = lpitem->hbmpItem;
1286
1287                 if (hbm == HBMMENU_CALLBACK)
1288                 {
1289                     DRAWITEMSTRUCT drawItem;
1290                     drawItem.CtlType = ODT_MENU;
1291                     drawItem.CtlID = 0;
1292                     drawItem.itemID = lpitem->wID;
1293                     drawItem.itemAction = odaction;
1294                     drawItem.itemState |= (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1295                     drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1296                     drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1297                     drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1298                     drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1299                     drawItem.hwndItem = (HWND)hmenu;
1300                     drawItem.hDC = hdc;
1301                     drawItem.rcItem = lpitem->rect;
1302                     drawItem.itemData = lpitem->dwItemData;
1303
1304                     if (!(lpitem->fState & MF_CHECKED))
1305                         SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1306
1307                 } else {
1308                     MENU_DrawBitmapItem(hdc, lpitem, &rect, FALSE, TRUE);
1309                 }
1310                 if (menu->dwStyle & MNS_CHECKORBMP)
1311                     rect.left += menu->maxBmpSize.cx - check_bitmap_width;
1312                 else
1313                     rect.left += menu->maxBmpSize.cx;
1314             }
1315             /* Draw the check mark
1316              *
1317              * FIXME:
1318              * Custom checkmark bitmaps are monochrome but not always 1bpp.
1319              */
1320             bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1321             if (bm)  /* we have a custom bitmap */
1322             {
1323                 HDC hdcMem = CreateCompatibleDC( hdc );
1324                 SelectObject( hdcMem, bm );
1325                 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1326                         check_bitmap_width, check_bitmap_height,
1327                         hdcMem, 0, 0, SRCCOPY );
1328                 DeleteDC( hdcMem );
1329             }
1330             else if (lpitem->fState & MF_CHECKED)  /* standard bitmaps */
1331             {
1332                 RECT r;
1333                 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1334                 HDC hdcMem = CreateCompatibleDC( hdc );
1335                 SelectObject( hdcMem, bm );
1336                 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1337                 DrawFrameControl( hdcMem, &r, DFC_MENU,
1338                                   (lpitem->fType & MFT_RADIOCHECK) ?
1339                                   DFCS_MENUBULLET : DFCS_MENUCHECK );
1340                 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1341                         hdcMem, 0, 0, SRCCOPY );
1342                 DeleteDC( hdcMem );
1343                 DeleteObject( bm );
1344             }
1345         }
1346
1347           /* Draw the popup-menu arrow */
1348         if (lpitem->fType & MF_POPUP)
1349         {
1350             HDC hdcMem = CreateCompatibleDC( hdc );
1351             HBITMAP hOrigBitmap;
1352
1353             hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1354             BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1355                       (y - arrow_bitmap_height) / 2,
1356                       arrow_bitmap_width, arrow_bitmap_height,
1357                       hdcMem, 0, 0, SRCCOPY );
1358             SelectObject( hdcMem, hOrigBitmap );
1359             DeleteDC( hdcMem );
1360         }
1361
1362         rect.left += check_bitmap_width;
1363         rect.right -= arrow_bitmap_width;
1364     }
1365
1366     /* Done for owner-drawn */
1367     if (lpitem->fType & MF_OWNERDRAW)
1368         return;
1369
1370     /* Draw the item text or bitmap */
1371     if (IS_BITMAP_ITEM(lpitem->fType))
1372     {
1373         MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar, FALSE);
1374         return;
1375     }
1376     /* No bitmap - process text if present */
1377     else if (IS_STRING_ITEM(lpitem->fType))
1378     {
1379         register int i;
1380         HFONT hfontOld = 0;
1381
1382         UINT uFormat = (menuBar) ?
1383                         DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1384                         DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1385
1386         if ( lpitem->fState & MFS_DEFAULT )
1387         {
1388              hfontOld = SelectObject( hdc, hMenuFontBold);
1389         }
1390
1391         if (menuBar)
1392         {
1393             rect.left += MENU_BAR_ITEMS_SPACE / 2;
1394             rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1395         }
1396
1397         for (i = 0; lpitem->text[i]; i++)
1398             if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1399                 break;
1400
1401         if(lpitem->fState & MF_GRAYED)
1402         {
1403             if (!(lpitem->fState & MF_HILITE) )
1404             {
1405                 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1406                 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1407                 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1408                 --rect.left; --rect.top; --rect.right; --rect.bottom;
1409             }
1410             SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1411         }
1412
1413         DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1414
1415         /* paint the shortcut text */
1416         if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
1417         {
1418             if (lpitem->text[i] == '\t')
1419             {
1420                 rect.left = lpitem->xTab;
1421                 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1422             }
1423             else
1424             {
1425                 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1426             }
1427
1428             if(lpitem->fState & MF_GRAYED)
1429             {
1430                 if (!(lpitem->fState & MF_HILITE) )
1431                 {
1432                     ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1433                     SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1434                     DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1435                     --rect.left; --rect.top; --rect.right; --rect.bottom;
1436                 }
1437                 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1438             }
1439             DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1440         }
1441
1442         if (hfontOld)
1443             SelectObject (hdc, hfontOld);
1444     }
1445 }
1446
1447
1448 /***********************************************************************
1449  *           MENU_DrawPopupMenu
1450  *
1451  * Paint a popup menu.
1452  */
1453 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1454 {
1455     HBRUSH hPrevBrush = 0;
1456     RECT rect;
1457
1458     TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1459
1460     GetClientRect( hwnd, &rect );
1461
1462     if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1463         && (SelectObject( hdc, hMenuFont)))
1464     {
1465         HPEN hPrevPen;
1466
1467         Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1468
1469         hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1470         if( hPrevPen )
1471         {
1472             POPUPMENU *menu;
1473
1474             DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1475
1476             /* draw menu items */
1477
1478             menu = MENU_GetMenu( hmenu );
1479             if (menu && menu->nItems)
1480             {
1481                 MENUITEM *item;
1482                 UINT u;
1483
1484                 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1485                     MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1486                                        menu->Height, FALSE, ODA_DRAWENTIRE );
1487
1488             }
1489         } else
1490         {
1491             SelectObject( hdc, hPrevBrush );
1492         }
1493     }
1494 }
1495
1496 /***********************************************************************
1497  *           MENU_DrawMenuBar
1498  *
1499  * Paint a menu bar. Returns the height of the menu bar.
1500  * called from [windows/nonclient.c]
1501  */
1502 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1503                          BOOL suppress_draw)
1504 {
1505     LPPOPUPMENU lppop;
1506     HFONT hfontOld = 0;
1507     HMENU hMenu = GetMenu(hwnd);
1508
1509     lppop = MENU_GetMenu( hMenu );
1510     if (lppop == NULL || lprect == NULL)
1511     {
1512         return GetSystemMetrics(SM_CYMENU);
1513     }
1514
1515     if (suppress_draw)
1516     {
1517         hfontOld = SelectObject( hDC, hMenuFont);
1518
1519         if (lppop->Height == 0)
1520                 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1521
1522         lprect->bottom = lprect->top + lppop->Height;
1523
1524         if (hfontOld) SelectObject( hDC, hfontOld);
1525         return lppop->Height;
1526     }
1527     else
1528         return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1529 }
1530
1531
1532 /***********************************************************************
1533  *           MENU_ShowPopup
1534  *
1535  * Display a popup menu.
1536  */
1537 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1538                               INT x, INT y, INT xanchor, INT yanchor )
1539 {
1540     POPUPMENU *menu;
1541     UINT width, height;
1542
1543     TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1544           hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1545
1546     if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1547     if (menu->FocusedItem != NO_SELECTED_ITEM)
1548     {
1549         menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1550         menu->FocusedItem = NO_SELECTED_ITEM;
1551     }
1552
1553     /* store the owner for DrawItem */
1554     menu->hwndOwner = hwndOwner;
1555
1556     MENU_PopupMenuCalcSize( menu, hwndOwner );
1557
1558     /* adjust popup menu pos so that it fits within the desktop */
1559
1560     width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1561     height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1562
1563     if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1564     {
1565         if( xanchor )
1566             x -= width - xanchor;
1567         if( x + width > GetSystemMetrics(SM_CXSCREEN))
1568             x = GetSystemMetrics(SM_CXSCREEN) - width;
1569     }
1570     if( x < 0 ) x = 0;
1571
1572     if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1573     {
1574         if( yanchor )
1575             y -= height + yanchor;
1576         if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1577             y = GetSystemMetrics(SM_CYSCREEN) - height;
1578     }
1579     if( y < 0 ) y = 0;
1580
1581     /* NOTE: In Windows, top menu popup is not owned. */
1582     menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1583                                 WS_POPUP, x, y, width, height,
1584                                 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1585                                 (LPVOID)hmenu );
1586     if( !menu->hWnd ) return FALSE;
1587     if (!top_popup) top_popup = menu->hWnd;
1588
1589     /* Display the window */
1590
1591     SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1592                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1593     UpdateWindow( menu->hWnd );
1594     return TRUE;
1595 }
1596
1597
1598 /***********************************************************************
1599  *           MENU_SelectItem
1600  */
1601 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1602                              BOOL sendMenuSelect, HMENU topmenu )
1603 {
1604     LPPOPUPMENU lppop;
1605     HDC hdc;
1606
1607     TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1608
1609     lppop = MENU_GetMenu( hmenu );
1610     if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1611
1612     if (lppop->FocusedItem == wIndex) return;
1613     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1614     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1615     if (!top_popup) top_popup = lppop->hWnd;
1616
1617     SelectObject( hdc, hMenuFont);
1618
1619       /* Clear previous highlighted item */
1620     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1621     {
1622         lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1623         MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1624                           lppop->Height, !(lppop->wFlags & MF_POPUP),
1625                           ODA_SELECT );
1626     }
1627
1628       /* Highlight new item (if any) */
1629     lppop->FocusedItem = wIndex;
1630     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1631     {
1632         if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1633             lppop->items[wIndex].fState |= MF_HILITE;
1634             MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1635                     &lppop->items[wIndex], lppop->Height,
1636                     !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1637         }
1638         if (sendMenuSelect)
1639         {
1640             MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1641             SendMessageW( hwndOwner, WM_MENUSELECT,
1642                      MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1643                      ip->fType | ip->fState |
1644                      (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1645         }
1646     }
1647     else if (sendMenuSelect) {
1648         if(topmenu){
1649             int pos;
1650             if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1651                 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1652                 MENUITEM *ip = &ptm->items[pos];
1653                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1654                          ip->fType | ip->fState |
1655                          (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1656             }
1657         }
1658     }
1659     ReleaseDC( lppop->hWnd, hdc );
1660 }
1661
1662
1663 /***********************************************************************
1664  *           MENU_MoveSelection
1665  *
1666  * Moves currently selected item according to the offset parameter.
1667  * If there is no selection then it should select the last item if
1668  * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1669  */
1670 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1671 {
1672     INT i;
1673     POPUPMENU *menu;
1674
1675     TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1676
1677     menu = MENU_GetMenu( hmenu );
1678     if ((!menu) || (!menu->items)) return;
1679
1680     if ( menu->FocusedItem != NO_SELECTED_ITEM )
1681     {
1682         if( menu->nItems == 1 ) return; else
1683         for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1684                                             ; i += offset)
1685             if (!(menu->items[i].fType & MF_SEPARATOR))
1686             {
1687                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1688                 return;
1689             }
1690     }
1691
1692     for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1693                   i >= 0 && i < menu->nItems ; i += offset)
1694         if (!(menu->items[i].fType & MF_SEPARATOR))
1695         {
1696             MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1697             return;
1698         }
1699 }
1700
1701
1702 /**********************************************************************
1703  *         MENU_SetItemData
1704  *
1705  * Set an item's flags, id and text ptr. Called by InsertMenu() and
1706  * ModifyMenu().
1707  */
1708 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1709                                 LPCWSTR str )
1710 {
1711     LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1712
1713     debug_print_menuitem("MENU_SetItemData from: ", item, "");
1714     TRACE("flags=%x str=%p\n", flags, str);
1715
1716     if (IS_STRING_ITEM(flags))
1717     {
1718         if (!str)
1719         {
1720             flags |= MF_SEPARATOR;
1721             item->text = NULL;
1722         }
1723         else
1724         {
1725             LPWSTR text;
1726             /* Item beginning with a backspace is a help item */
1727             if (*str == '\b')
1728             {
1729                 flags |= MF_HELP;
1730                 str++;
1731             }
1732             if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1733                 return FALSE;
1734             strcpyW( text, str );
1735             item->text = text;
1736         }
1737     }
1738     else if (IS_BITMAP_ITEM(flags))
1739         item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1740     else item->text = NULL;
1741
1742     if (flags & MF_OWNERDRAW)
1743         item->dwItemData = (DWORD)str;
1744     else
1745         item->dwItemData = 0;
1746
1747     if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1748         DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */
1749
1750     if (flags & MF_POPUP)
1751     {
1752         POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1753         if (menu) menu->wFlags |= MF_POPUP;
1754         else
1755         {
1756             item->wID = 0;
1757             item->hSubMenu = 0;
1758             item->fType = 0;
1759             item->fState = 0;
1760             return FALSE;
1761         }
1762     }
1763
1764     item->wID = id;
1765     if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1766
1767     if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1768       flags |= MF_POPUP; /* keep popup */
1769
1770     item->fType = flags & TYPE_MASK;
1771     item->fState = (flags & STATE_MASK) &
1772         ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1773
1774
1775     /* Don't call SetRectEmpty here! */
1776
1777
1778     HeapFree( GetProcessHeap(), 0, prevText );
1779
1780     debug_print_menuitem("MENU_SetItemData to  : ", item, "");
1781     return TRUE;
1782 }
1783
1784
1785 /**********************************************************************
1786  *         MENU_InsertItem
1787  *
1788  * Insert (allocate) a new item into a menu.
1789  */
1790 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1791 {
1792     MENUITEM *newItems;
1793     POPUPMENU *menu;
1794
1795     if (!(menu = MENU_GetMenu(hMenu)))
1796         return NULL;
1797
1798     /* Find where to insert new item */
1799
1800     if (flags & MF_BYPOSITION) {
1801         if (pos > menu->nItems)
1802             pos = menu->nItems;
1803     } else {
1804         if (!MENU_FindItem( &hMenu, &pos, flags ))
1805             pos = menu->nItems;
1806         else {
1807             if (!(menu = MENU_GetMenu( hMenu )))
1808                 return NULL;
1809         }
1810     }
1811
1812     /* Create new items array */
1813
1814     newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1815     if (!newItems)
1816     {
1817         WARN("allocation failed\n" );
1818         return NULL;
1819     }
1820     if (menu->nItems > 0)
1821     {
1822           /* Copy the old array into the new one */
1823         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1824         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1825                                         (menu->nItems-pos)*sizeof(MENUITEM) );
1826         HeapFree( GetProcessHeap(), 0, menu->items );
1827     }
1828     menu->items = newItems;
1829     menu->nItems++;
1830     memset( &newItems[pos], 0, sizeof(*newItems) );
1831     menu->Height = 0; /* force size recalculate */
1832     return &newItems[pos];
1833 }
1834
1835
1836 /**********************************************************************
1837  *         MENU_ParseResource
1838  *
1839  * Parse a standard menu resource and add items to the menu.
1840  * Return a pointer to the end of the resource.
1841  *
1842  * NOTE: flags is equivalent to the mtOption field
1843  */
1844 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1845 {
1846     WORD flags, id = 0;
1847     LPCSTR str;
1848
1849     do
1850     {
1851         flags = GET_WORD(res);
1852         res += sizeof(WORD);
1853         if (!(flags & MF_POPUP))
1854         {
1855             id = GET_WORD(res);
1856             res += sizeof(WORD);
1857         }
1858         str = res;
1859         if (!unicode) res += strlen(str) + 1;
1860         else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1861         if (flags & MF_POPUP)
1862         {
1863             HMENU hSubMenu = CreatePopupMenu();
1864             if (!hSubMenu) return NULL;
1865             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1866                 return NULL;
1867             if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1868             else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1869         }
1870         else  /* Not a popup */
1871         {
1872             if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1873             else AppendMenuW( hMenu, flags, id,
1874                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1875         }
1876     } while (!(flags & MF_END));
1877     return res;
1878 }
1879
1880
1881 /**********************************************************************
1882  *         MENUEX_ParseResource
1883  *
1884  * Parse an extended menu resource and add items to the menu.
1885  * Return a pointer to the end of the resource.
1886  */
1887 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1888 {
1889     WORD resinfo;
1890     do {
1891         MENUITEMINFOW mii;
1892
1893         mii.cbSize = sizeof(mii);
1894         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1895         mii.fType = GET_DWORD(res);
1896         res += sizeof(DWORD);
1897         mii.fState = GET_DWORD(res);
1898         res += sizeof(DWORD);
1899         mii.wID = GET_DWORD(res);
1900         res += sizeof(DWORD);
1901         resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
1902         res += sizeof(WORD);
1903         /* Align the text on a word boundary.  */
1904         res += (~((int)res - 1)) & 1;
1905         mii.dwTypeData = (LPWSTR) res;
1906         res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1907         /* Align the following fields on a dword boundary.  */
1908         res += (~((int)res - 1)) & 3;
1909
1910         TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1911               mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1912
1913         if (resinfo & 1) {      /* Pop-up? */
1914             /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
1915             res += sizeof(DWORD);
1916             mii.hSubMenu = CreatePopupMenu();
1917             if (!mii.hSubMenu)
1918                 return NULL;
1919             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1920                 DestroyMenu(mii.hSubMenu);
1921                 return NULL;
1922             }
1923             mii.fMask |= MIIM_SUBMENU;
1924             mii.fType |= MF_POPUP;
1925         }
1926         else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1927         {
1928             WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1929                 mii.wID, mii.fType);
1930             mii.fType |= MF_SEPARATOR;
1931         }
1932         InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1933     } while (!(resinfo & MF_END));
1934     return res;
1935 }
1936
1937
1938 /***********************************************************************
1939  *           MENU_GetSubPopup
1940  *
1941  * Return the handle of the selected sub-popup menu (if any).
1942  */
1943 static HMENU MENU_GetSubPopup( HMENU hmenu )
1944 {
1945     POPUPMENU *menu;
1946     MENUITEM *item;
1947
1948     menu = MENU_GetMenu( hmenu );
1949
1950     if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1951
1952     item = &menu->items[menu->FocusedItem];
1953     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1954         return item->hSubMenu;
1955     return 0;
1956 }
1957
1958
1959 /***********************************************************************
1960  *           MENU_HideSubPopups
1961  *
1962  * Hide the sub-popup menus of this menu.
1963  */
1964 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1965                                 BOOL sendMenuSelect )
1966 {
1967     POPUPMENU *menu = MENU_GetMenu( hmenu );
1968
1969     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1970
1971     if (menu && top_popup)
1972     {
1973         HMENU hsubmenu;
1974         POPUPMENU *submenu;
1975         MENUITEM *item;
1976
1977         if (menu->FocusedItem != NO_SELECTED_ITEM)
1978         {
1979             item = &menu->items[menu->FocusedItem];
1980             if (!(item->fType & MF_POPUP) ||
1981                 !(item->fState & MF_MOUSESELECT)) return;
1982             item->fState &= ~MF_MOUSESELECT;
1983             hsubmenu = item->hSubMenu;
1984         } else return;
1985
1986         submenu = MENU_GetMenu( hsubmenu );
1987         MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1988         MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
1989         DestroyWindow( submenu->hWnd );
1990         submenu->hWnd = 0;
1991     }
1992 }
1993
1994
1995 /***********************************************************************
1996  *           MENU_ShowSubPopup
1997  *
1998  * Display the sub-menu of the selected item of this menu.
1999  * Return the handle of the submenu, or hmenu if no submenu to display.
2000  */
2001 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2002                                   BOOL selectFirst, UINT wFlags )
2003 {
2004     RECT rect;
2005     POPUPMENU *menu;
2006     MENUITEM *item;
2007     HDC hdc;
2008
2009     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2010
2011     if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2012
2013     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2014
2015     item = &menu->items[menu->FocusedItem];
2016     if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2017         return hmenu;
2018
2019     /* message must be sent before using item,
2020        because nearly everything may be changed by the application ! */
2021
2022     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2023     if (!(wFlags & TPM_NONOTIFY))
2024        SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2025                      MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2026
2027     item = &menu->items[menu->FocusedItem];
2028     rect = item->rect;
2029
2030     /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2031     if (!(item->fState & MF_HILITE))
2032     {
2033         if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2034         else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2035
2036         SelectObject( hdc, hMenuFont);
2037
2038         item->fState |= MF_HILITE;
2039         MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2040         ReleaseDC( menu->hWnd, hdc );
2041     }
2042     if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2043       item->rect = rect;
2044
2045     item->fState |= MF_MOUSESELECT;
2046
2047     if (IS_SYSTEM_MENU(menu))
2048     {
2049         MENU_InitSysMenuPopup(item->hSubMenu,
2050                               GetWindowLongW( menu->hWnd, GWL_STYLE ),
2051                               GetClassLongW( menu->hWnd, GCL_STYLE));
2052
2053         NC_GetSysPopupPos( menu->hWnd, &rect );
2054         rect.top = rect.bottom;
2055         rect.right = GetSystemMetrics(SM_CXSIZE);
2056         rect.bottom = GetSystemMetrics(SM_CYSIZE);
2057     }
2058     else
2059     {
2060         GetWindowRect( menu->hWnd, &rect );
2061         if (menu->wFlags & MF_POPUP)
2062         {
2063             rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2064             rect.top += item->rect.top;
2065             rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2066             rect.bottom = item->rect.top - item->rect.bottom;
2067         }
2068         else
2069         {
2070             rect.left += item->rect.left;
2071             rect.top += item->rect.bottom;
2072             rect.right = item->rect.right - item->rect.left;
2073             rect.bottom = item->rect.bottom - item->rect.top;
2074         }
2075     }
2076
2077     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2078                     rect.left, rect.top, rect.right, rect.bottom );
2079     if (selectFirst)
2080         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2081     return item->hSubMenu;
2082 }
2083
2084
2085
2086 /**********************************************************************
2087  *         MENU_IsMenuActive
2088  */
2089 HWND MENU_IsMenuActive(void)
2090 {
2091     return top_popup;
2092 }
2093
2094 /***********************************************************************
2095  *           MENU_PtMenu
2096  *
2097  * Walks menu chain trying to find a menu pt maps to.
2098  */
2099 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2100 {
2101    POPUPMENU *menu = MENU_GetMenu( hMenu );
2102    UINT item = menu->FocusedItem;
2103    HMENU ret;
2104
2105    /* try subpopup first (if any) */
2106    ret = (item != NO_SELECTED_ITEM &&
2107           (menu->items[item].fType & MF_POPUP) &&
2108           (menu->items[item].fState & MF_MOUSESELECT))
2109         ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2110
2111    if (!ret)  /* check the current window (avoiding WM_HITTEST) */
2112    {
2113        INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2114        if( menu->wFlags & MF_POPUP )
2115        {
2116            if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2117        }
2118        else if (ht == HTSYSMENU)
2119            ret = get_win_sys_menu( menu->hWnd );
2120        else if (ht == HTMENU)
2121            ret = GetMenu( menu->hWnd );
2122    }
2123    return ret;
2124 }
2125
2126 /***********************************************************************
2127  *           MENU_ExecFocusedItem
2128  *
2129  * Execute a menu item (for instance when user pressed Enter).
2130  * Return the wID of the executed item. Otherwise, -1 indicating
2131  * that no menu item was executed;
2132  * Have to receive the flags for the TrackPopupMenu options to avoid
2133  * sending unwanted message.
2134  *
2135  */
2136 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2137 {
2138     MENUITEM *item;
2139     POPUPMENU *menu = MENU_GetMenu( hMenu );
2140
2141     TRACE("%p hmenu=%p\n", pmt, hMenu);
2142
2143     if (!menu || !menu->nItems ||
2144         (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2145
2146     item = &menu->items[menu->FocusedItem];
2147
2148     TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2149
2150     if (!(item->fType & MF_POPUP))
2151     {
2152         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2153         {
2154             /* If TPM_RETURNCMD is set you return the id, but
2155                do not send a message to the owner */
2156             if(!(wFlags & TPM_RETURNCMD))
2157             {
2158                 if( menu->wFlags & MF_SYSMENU )
2159                     PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2160                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2161                 else
2162                     PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2163             }
2164             return item->wID;
2165         }
2166     }
2167     else
2168         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2169
2170     return -1;
2171 }
2172
2173 /***********************************************************************
2174  *           MENU_SwitchTracking
2175  *
2176  * Helper function for menu navigation routines.
2177  */
2178 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2179 {
2180     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2181     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2182
2183     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2184
2185     if( pmt->hTopMenu != hPtMenu &&
2186         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2187     {
2188         /* both are top level menus (system and menu-bar) */
2189         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2190         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2191         pmt->hTopMenu = hPtMenu;
2192     }
2193     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2194     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2195 }
2196
2197
2198 /***********************************************************************
2199  *           MENU_ButtonDown
2200  *
2201  * Return TRUE if we can go on with menu tracking.
2202  */
2203 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2204 {
2205     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2206
2207     if (hPtMenu)
2208     {
2209         UINT id = 0;
2210         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2211         MENUITEM *item;
2212
2213         if( IS_SYSTEM_MENU(ptmenu) )
2214             item = ptmenu->items;
2215         else
2216             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2217
2218         if( item )
2219         {
2220             if( ptmenu->FocusedItem != id )
2221                 MENU_SwitchTracking( pmt, hPtMenu, id );
2222
2223             /* If the popup menu is not already "popped" */
2224             if(!(item->fState & MF_MOUSESELECT ))
2225             {
2226                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2227             }
2228
2229             return TRUE;
2230         }
2231         /* Else the click was on the menu bar, finish the tracking */
2232     }
2233     return FALSE;
2234 }
2235
2236 /***********************************************************************
2237  *           MENU_ButtonUp
2238  *
2239  * Return the value of MENU_ExecFocusedItem if
2240  * the selected item was not a popup. Else open the popup.
2241  * A -1 return value indicates that we go on with menu tracking.
2242  *
2243  */
2244 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2245 {
2246     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2247
2248     if (hPtMenu)
2249     {
2250         UINT id = 0;
2251         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2252         MENUITEM *item;
2253
2254         if( IS_SYSTEM_MENU(ptmenu) )
2255             item = ptmenu->items;
2256         else
2257             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2258
2259         if( item && (ptmenu->FocusedItem == id ))
2260         {
2261             if( !(item->fType & MF_POPUP) )
2262                 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2263
2264             /* If we are dealing with the top-level menu            */
2265             /* and this is a click on an already "popped" item:     */
2266             /* Stop the menu tracking and close the opened submenus */
2267             if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2268                 return 0;
2269         }
2270         ptmenu->bTimeToHide = TRUE;
2271     }
2272     return -1;
2273 }
2274
2275
2276 /***********************************************************************
2277  *           MENU_MouseMove
2278  *
2279  * Return TRUE if we can go on with menu tracking.
2280  */
2281 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2282 {
2283     UINT id = NO_SELECTED_ITEM;
2284     POPUPMENU *ptmenu = NULL;
2285
2286     if( hPtMenu )
2287     {
2288         ptmenu = MENU_GetMenu( hPtMenu );
2289         if( IS_SYSTEM_MENU(ptmenu) )
2290             id = 0;
2291         else
2292             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2293     }
2294
2295     if( id == NO_SELECTED_ITEM )
2296     {
2297         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2298                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2299
2300     }
2301     else if( ptmenu->FocusedItem != id )
2302     {
2303             MENU_SwitchTracking( pmt, hPtMenu, id );
2304             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2305     }
2306     return TRUE;
2307 }
2308
2309
2310 /***********************************************************************
2311  *           MENU_SetCapture
2312  */
2313 static void MENU_SetCapture( HWND hwnd )
2314 {
2315     HWND previous = 0;
2316
2317     SERVER_START_REQ( set_capture_window )
2318     {
2319         req->handle = hwnd;
2320         req->flags  = CAPTURE_MENU;
2321         if (!wine_server_call_err( req ))
2322         {
2323             previous = reply->previous;
2324             hwnd = reply->full_handle;
2325         }
2326     }
2327     SERVER_END_REQ;
2328
2329     if (previous && previous != hwnd)
2330         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2331 }
2332
2333
2334 /***********************************************************************
2335  *           MENU_DoNextMenu
2336  *
2337  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2338  */
2339 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2340 {
2341     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2342
2343     if( (vk == VK_LEFT &&  menu->FocusedItem == 0 ) ||
2344         (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2345     {
2346         MDINEXTMENU next_menu;
2347         HMENU hNewMenu;
2348         HWND  hNewWnd;
2349         UINT  id = 0;
2350
2351         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2352         next_menu.hmenuNext = 0;
2353         next_menu.hwndNext = 0;
2354         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2355
2356         TRACE("%p [%p] -> %p [%p]\n",
2357               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2358
2359         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2360         {
2361             DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2362             hNewWnd = pmt->hOwnerWnd;
2363             if( IS_SYSTEM_MENU(menu) )
2364             {
2365                 /* switch to the menu bar */
2366
2367                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2368
2369                 if( vk == VK_LEFT )
2370                 {
2371                     menu = MENU_GetMenu( hNewMenu );
2372                     id = menu->nItems - 1;
2373                 }
2374             }
2375             else if (style & WS_SYSMENU )
2376             {
2377                 /* switch to the system menu */
2378                 hNewMenu = get_win_sys_menu( hNewWnd );
2379             }
2380             else return FALSE;
2381         }
2382         else    /* application returned a new menu to switch to */
2383         {
2384             hNewMenu = next_menu.hmenuNext;
2385             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2386
2387             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2388             {
2389                 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2390
2391                 if (style & WS_SYSMENU &&
2392                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2393                 {
2394                     /* get the real system menu */
2395                     hNewMenu =  get_win_sys_menu(hNewWnd);
2396                 }
2397                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2398                 {
2399                     /* FIXME: Not sure what to do here;
2400                      * perhaps try to track hNewMenu as a popup? */
2401
2402                     TRACE(" -- got confused.\n");
2403                     return FALSE;
2404                 }
2405             }
2406             else return FALSE;
2407         }
2408
2409         if( hNewMenu != pmt->hTopMenu )
2410         {
2411             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2412                     FALSE, 0 );
2413             if( pmt->hCurrentMenu != pmt->hTopMenu )
2414                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2415         }
2416
2417         if( hNewWnd != pmt->hOwnerWnd )
2418         {
2419             pmt->hOwnerWnd = hNewWnd;
2420             MENU_SetCapture( pmt->hOwnerWnd );
2421         }
2422
2423         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2424         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2425
2426         return TRUE;
2427     }
2428     return FALSE;
2429 }
2430
2431 /***********************************************************************
2432  *           MENU_SuspendPopup
2433  *
2434  * The idea is not to show the popup if the next input message is
2435  * going to hide it anyway.
2436  */
2437 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2438 {
2439     MSG msg;
2440
2441     msg.hwnd = pmt->hOwnerWnd;
2442
2443     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2444     pmt->trackFlags |= TF_SKIPREMOVE;
2445
2446     switch( uMsg )
2447     {
2448         case WM_KEYDOWN:
2449              PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2450              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2451              {
2452                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2453                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2454                  if( msg.message == WM_KEYDOWN &&
2455                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2456                  {
2457                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2458                      return TRUE;
2459                  }
2460              }
2461              break;
2462     }
2463
2464     /* failures go through this */
2465     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2466     return FALSE;
2467 }
2468
2469 /***********************************************************************
2470  *           MENU_KeyEscape
2471  *
2472  * Handle a VK_ESCAPE key event in a menu.
2473  */
2474 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2475 {
2476     BOOL bEndMenu = TRUE;
2477
2478     if (pmt->hCurrentMenu != pmt->hTopMenu)
2479     {
2480         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2481
2482         if (menu->wFlags & MF_POPUP)
2483         {
2484             HMENU hmenutmp, hmenuprev;
2485
2486             hmenuprev = hmenutmp = pmt->hTopMenu;
2487
2488             /* close topmost popup */
2489             while (hmenutmp != pmt->hCurrentMenu)
2490             {
2491                 hmenuprev = hmenutmp;
2492                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2493             }
2494
2495             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2496             pmt->hCurrentMenu = hmenuprev;
2497             bEndMenu = FALSE;
2498         }
2499     }
2500
2501     return bEndMenu;
2502 }
2503
2504 /***********************************************************************
2505  *           MENU_KeyLeft
2506  *
2507  * Handle a VK_LEFT key event in a menu.
2508  */
2509 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2510 {
2511     POPUPMENU *menu;
2512     HMENU hmenutmp, hmenuprev;
2513     UINT  prevcol;
2514
2515     hmenuprev = hmenutmp = pmt->hTopMenu;
2516     menu = MENU_GetMenu( hmenutmp );
2517
2518     /* Try to move 1 column left (if possible) */
2519     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2520         NO_SELECTED_ITEM ) {
2521
2522         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2523                          prevcol, TRUE, 0 );
2524         return;
2525     }
2526
2527     /* close topmost popup */
2528     while (hmenutmp != pmt->hCurrentMenu)
2529     {
2530         hmenuprev = hmenutmp;
2531         hmenutmp = MENU_GetSubPopup( hmenuprev );
2532     }
2533
2534     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2535     pmt->hCurrentMenu = hmenuprev;
2536
2537     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2538     {
2539         /* move menu bar selection if no more popups are left */
2540
2541         if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2542              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2543
2544         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2545         {
2546            /* A sublevel menu was displayed - display the next one
2547             * unless there is another displacement coming up */
2548
2549             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2550                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2551                                                 pmt->hTopMenu, TRUE, wFlags);
2552         }
2553     }
2554 }
2555
2556
2557 /***********************************************************************
2558  *           MENU_KeyRight
2559  *
2560  * Handle a VK_RIGHT key event in a menu.
2561  */
2562 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2563 {
2564     HMENU hmenutmp;
2565     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2566     UINT  nextcol;
2567
2568     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2569           pmt->hCurrentMenu,
2570           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2571           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2572
2573     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2574     {
2575         /* If already displaying a popup, try to display sub-popup */
2576
2577         hmenutmp = pmt->hCurrentMenu;
2578         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2579
2580         /* if subpopup was displayed then we are done */
2581         if (hmenutmp != pmt->hCurrentMenu) return;
2582     }
2583
2584     /* Check to see if there's another column */
2585     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2586         NO_SELECTED_ITEM ) {
2587         TRACE("Going to %d.\n", nextcol );
2588         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2589                          nextcol, TRUE, 0 );
2590         return;
2591     }
2592
2593     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2594     {
2595         if( pmt->hCurrentMenu != pmt->hTopMenu )
2596         {
2597             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2598             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2599         } else hmenutmp = 0;
2600
2601         /* try to move to the next item */
2602         if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2603              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2604
2605         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2606             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2607                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2608                                                        pmt->hTopMenu, TRUE, wFlags);
2609     }
2610 }
2611
2612 /***********************************************************************
2613  *           MENU_TrackMenu
2614  *
2615  * Menu tracking code.
2616  */
2617 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2618                             HWND hwnd, const RECT *lprect )
2619 {
2620     MSG msg;
2621     POPUPMENU *menu;
2622     BOOL fRemove;
2623     INT executedMenuId = -1;
2624     MTRACKER mt;
2625     BOOL enterIdleSent = FALSE;
2626
2627     mt.trackFlags = 0;
2628     mt.hCurrentMenu = hmenu;
2629     mt.hTopMenu = hmenu;
2630     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2631     mt.pt.x = x;
2632     mt.pt.y = y;
2633
2634     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2635           hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2636           (lprect) ? lprect->right : 0,  (lprect) ? lprect->bottom : 0);
2637
2638     fEndMenu = FALSE;
2639     if (!(menu = MENU_GetMenu( hmenu )))
2640     {
2641         WARN("Invalid menu handle %p\n", hmenu);
2642         SetLastError(ERROR_INVALID_MENU_HANDLE);
2643         return FALSE;
2644     }
2645
2646     if (wFlags & TPM_BUTTONDOWN)
2647     {
2648         /* Get the result in order to start the tracking or not */
2649         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2650         fEndMenu = !fRemove;
2651     }
2652
2653     if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2654
2655     MENU_SetCapture( mt.hOwnerWnd );
2656
2657     while (!fEndMenu)
2658     {
2659         menu = MENU_GetMenu( mt.hCurrentMenu );
2660         if (!menu) /* sometimes happens if I do a window manager close */
2661             break;
2662
2663         /* we have to keep the message in the queue until it's
2664          * clear that menu loop is not over yet. */
2665
2666         for (;;)
2667         {
2668             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2669             {
2670                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2671                 /* remove the message from the queue */
2672                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2673             }
2674             else
2675             {
2676                 if (!enterIdleSent)
2677                 {
2678                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2679                     enterIdleSent = TRUE;
2680                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2681                 }
2682                 WaitMessage();
2683             }
2684         }
2685
2686         /* check if EndMenu() tried to cancel us, by posting this message */
2687         if(msg.message == WM_CANCELMODE)
2688         {
2689             /* we are now out of the loop */
2690             fEndMenu = TRUE;
2691
2692             /* remove the message from the queue */
2693             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2694
2695             /* break out of internal loop, ala ESCAPE */
2696             break;
2697         }
2698
2699         TranslateMessage( &msg );
2700         mt.pt = msg.pt;
2701
2702         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2703           enterIdleSent=FALSE;
2704
2705         fRemove = FALSE;
2706         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2707         {
2708             /*
2709              * Use the mouse coordinates in lParam instead of those in the MSG
2710              * struct to properly handle synthetic messages. They are already
2711              * in screen coordinates.
2712              */
2713             mt.pt.x = (short)LOWORD(msg.lParam);
2714             mt.pt.y = (short)HIWORD(msg.lParam);
2715
2716             /* Find a menu for this mouse event */
2717             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2718
2719             switch(msg.message)
2720             {
2721                 /* no WM_NC... messages in captured state */
2722
2723                 case WM_RBUTTONDBLCLK:
2724                 case WM_RBUTTONDOWN:
2725                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2726                     /* fall through */
2727                 case WM_LBUTTONDBLCLK:
2728                 case WM_LBUTTONDOWN:
2729                     /* If the message belongs to the menu, removes it from the queue */
2730                     /* Else, end menu tracking */
2731                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2732                     fEndMenu = !fRemove;
2733                     break;
2734
2735                 case WM_RBUTTONUP:
2736                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2737                     /* fall through */
2738                 case WM_LBUTTONUP:
2739                     /* Check if a menu was selected by the mouse */
2740                     if (hmenu)
2741                     {
2742                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2743
2744                         /* End the loop if executedMenuId is an item ID */
2745                         /* or if the job was done (executedMenuId = 0). */
2746                         fEndMenu = fRemove = (executedMenuId != -1);
2747                     }
2748                     /* No menu was selected by the mouse */
2749                     /* if the function was called by TrackPopupMenu, continue
2750                        with the menu tracking. If not, stop it */
2751                     else
2752                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2753
2754                     break;
2755
2756                 case WM_MOUSEMOVE:
2757                     /* the selected menu item must be changed every time */
2758                      /* the mouse moves. */
2759
2760                     if (hmenu)
2761                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2762
2763             } /* switch(msg.message) - mouse */
2764         }
2765         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2766         {
2767             fRemove = TRUE;  /* Keyboard messages are always removed */
2768             switch(msg.message)
2769             {
2770             case WM_KEYDOWN:
2771             case WM_SYSKEYDOWN:
2772                 switch(msg.wParam)
2773                 {
2774                 case VK_MENU:
2775                     fEndMenu = TRUE;
2776                     break;
2777
2778                 case VK_HOME:
2779                 case VK_END:
2780                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2781                                      NO_SELECTED_ITEM, FALSE, 0 );
2782                 /* fall through */
2783                 case VK_UP:
2784                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2785                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2786                     break;
2787
2788                 case VK_DOWN: /* If on menu bar, pull-down the menu */
2789
2790                     menu = MENU_GetMenu( mt.hCurrentMenu );
2791                     if (!(menu->wFlags & MF_POPUP))
2792                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2793                     else      /* otherwise try to move selection */
2794                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2795                     break;
2796
2797                 case VK_LEFT:
2798                     MENU_KeyLeft( &mt, wFlags );
2799                     break;
2800
2801                 case VK_RIGHT:
2802                     MENU_KeyRight( &mt, wFlags );
2803                     break;
2804
2805                 case VK_ESCAPE:
2806                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
2807                     break;
2808
2809                 case VK_F1:
2810                     {
2811                         HELPINFO hi;
2812                         hi.cbSize = sizeof(HELPINFO);
2813                         hi.iContextType = HELPINFO_MENUITEM;
2814                         if (menu->FocusedItem == NO_SELECTED_ITEM)
2815                             hi.iCtrlId = 0;
2816                         else
2817                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2818                         hi.hItemHandle = hmenu;
2819                         hi.dwContextId = menu->dwContextHelpID;
2820                         hi.MousePos = msg.pt;
2821                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2822                         break;
2823                     }
2824
2825                 default:
2826                     break;
2827                 }
2828                 break;  /* WM_KEYDOWN */
2829
2830             case WM_CHAR:
2831             case WM_SYSCHAR:
2832                 {
2833                     UINT        pos;
2834
2835                     if (msg.wParam == '\r' || msg.wParam == ' ')
2836                     {
2837                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2838                         fEndMenu = (executedMenuId != -1);
2839
2840                         break;
2841                     }
2842
2843                       /* Hack to avoid control chars. */
2844                       /* We will find a better way real soon... */
2845                     if (msg.wParam < 32) break;
2846
2847                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2848                                               LOWORD(msg.wParam), FALSE );
2849                     if (pos == (UINT)-2) fEndMenu = TRUE;
2850                     else if (pos == (UINT)-1) MessageBeep(0);
2851                     else
2852                     {
2853                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2854                                 TRUE, 0 );
2855                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2856                         fEndMenu = (executedMenuId != -1);
2857                     }
2858                 }
2859                 break;
2860             }  /* switch(msg.message) - kbd */
2861         }
2862         else
2863         {
2864             DispatchMessageW( &msg );
2865         }
2866
2867         if (!fEndMenu) fRemove = TRUE;
2868
2869         /* finally remove message from the queue */
2870
2871         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2872             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2873         else mt.trackFlags &= ~TF_SKIPREMOVE;
2874     }
2875
2876     MENU_SetCapture(0);  /* release the capture */
2877
2878     /* If dropdown is still painted and the close box is clicked on
2879        then the menu will be destroyed as part of the DispatchMessage above.
2880        This will then invalidate the menu handle in mt.hTopMenu. We should
2881        check for this first.  */
2882     if( IsMenu( mt.hTopMenu ) )
2883     {
2884         menu = MENU_GetMenu( mt.hTopMenu );
2885
2886         if( IsWindow( mt.hOwnerWnd ) )
2887         {
2888             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2889
2890             if (menu && (menu->wFlags & MF_POPUP))
2891             {
2892                 DestroyWindow( menu->hWnd );
2893                 menu->hWnd = 0;
2894             }
2895             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2896             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2897         }
2898
2899         /* Reset the variable for hiding menu */
2900         if( menu ) menu->bTimeToHide = FALSE;
2901     }
2902
2903     /* The return value is only used by TrackPopupMenu */
2904     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2905     if (executedMenuId == -1) executedMenuId = 0;
2906     return executedMenuId;
2907 }
2908
2909 /***********************************************************************
2910  *           MENU_InitTracking
2911  */
2912 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2913 {
2914     POPUPMENU *menu;
2915     
2916     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2917
2918     HideCaret(0);
2919
2920     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2921     if (!(wFlags & TPM_NONOTIFY))
2922        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2923
2924     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2925
2926     if (!(wFlags & TPM_NONOTIFY))
2927     {
2928        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2929        /* If an app changed/recreated menu bar entries in WM_INITMENU
2930         * menu sizes will be recalculated once the menu created/shown.
2931         */
2932     }
2933     
2934     /* This makes the menus of applications built with Delphi work.
2935      * It also enables menus to be displayed in more than one window,
2936      * but there are some bugs left that need to be fixed in this case.
2937      */
2938     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2939     
2940     return TRUE;
2941 }
2942 /***********************************************************************
2943  *           MENU_ExitTracking
2944  */
2945 static BOOL MENU_ExitTracking(HWND hWnd)
2946 {
2947     TRACE("hwnd=%p\n", hWnd);
2948
2949     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2950     ShowCaret(0);
2951     top_popup = 0;
2952     return TRUE;
2953 }
2954
2955 /***********************************************************************
2956  *           MENU_TrackMouseMenuBar
2957  *
2958  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2959  */
2960 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2961 {
2962     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2963     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2964
2965     TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2966
2967     if (IsMenu(hMenu))
2968     {
2969         /* map point to parent client coordinates */
2970         HWND parent = GetAncestor( hWnd, GA_PARENT );
2971         if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2972
2973         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2974         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2975         MENU_ExitTracking(hWnd);
2976     }
2977 }
2978
2979
2980 /***********************************************************************
2981  *           MENU_TrackKbdMenuBar
2982  *
2983  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2984  */
2985 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2986 {
2987     UINT uItem = NO_SELECTED_ITEM;
2988     HMENU hTrackMenu;
2989     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2990
2991     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2992
2993     /* find window that has a menu */
2994
2995     while (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)
2996         if (!(hwnd = GetParent( hwnd ))) return;
2997
2998     /* check if we have to track a system menu */
2999
3000     hTrackMenu = GetMenu( hwnd );
3001     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3002     {
3003         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3004         hTrackMenu = get_win_sys_menu( hwnd );
3005         uItem = 0;
3006         wParam |= HTSYSMENU; /* prevent item lookup */
3007     }
3008
3009     if (!IsMenu( hTrackMenu )) return;
3010
3011     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3012
3013     if( wChar && wChar != ' ' )
3014     {
3015         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3016         if ( uItem >= (UINT)(-2) )
3017         {
3018             if( uItem == (UINT)(-1) ) MessageBeep(0);
3019             /* schedule end of menu tracking */
3020             wFlags |= TF_ENDMENU;
3021             goto track_menu;
3022         }
3023     }
3024
3025     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3026
3027     if (wParam & HTSYSMENU)
3028     {
3029         /* prevent sysmenu activation for managed windows on Alt down/up */
3030         if (GetPropA( hwnd, "__wine_x11_managed" ))
3031             wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3032     }
3033     else
3034     {
3035         if( uItem == NO_SELECTED_ITEM )
3036             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3037         else
3038             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3039     }
3040
3041 track_menu:
3042     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3043     MENU_ExitTracking( hwnd );
3044 }
3045
3046
3047 /**********************************************************************
3048  *           TrackPopupMenu   (USER32.@)
3049  *
3050  * Like the win32 API, the function return the command ID only if the
3051  * flag TPM_RETURNCMD is on.
3052  *
3053  */
3054 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3055                            INT nReserved, HWND hWnd, const RECT *lpRect )
3056 {
3057     BOOL ret = FALSE;
3058
3059     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3060
3061     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3062     if (!(wFlags & TPM_NONOTIFY))
3063         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3064
3065     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3066         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3067     MENU_ExitTracking(hWnd);
3068
3069     return ret;
3070 }
3071
3072 /**********************************************************************
3073  *           TrackPopupMenuEx   (USER32.@)
3074  */
3075 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3076                                 HWND hWnd, LPTPMPARAMS lpTpm )
3077 {
3078     FIXME("not fully implemented\n" );
3079     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3080                              lpTpm ? &lpTpm->rcExclude : NULL );
3081 }
3082
3083 /***********************************************************************
3084  *           PopupMenuWndProc
3085  *
3086  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3087  */
3088 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3089 {
3090     TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3091
3092     switch(message)
3093     {
3094     case WM_CREATE:
3095         {
3096             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3097             SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3098             return 0;
3099         }
3100
3101     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3102         return MA_NOACTIVATE;
3103
3104     case WM_PAINT:
3105         {
3106             PAINTSTRUCT ps;
3107             BeginPaint( hwnd, &ps );
3108             MENU_DrawPopupMenu( hwnd, ps.hdc,
3109                                 (HMENU)GetWindowLongW( hwnd, 0 ) );
3110             EndPaint( hwnd, &ps );
3111             return 0;
3112         }
3113     case WM_ERASEBKGND:
3114         return 1;
3115
3116     case WM_DESTROY:
3117         /* zero out global pointer in case resident popup window was destroyed. */
3118         if (hwnd == top_popup) top_popup = 0;
3119         break;
3120
3121     case WM_SHOWWINDOW:
3122
3123         if( wParam )
3124         {
3125             if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3126         }
3127         else
3128             SetWindowLongW( hwnd, 0, 0 );
3129         break;
3130
3131     case MM_SETMENUHANDLE:
3132         SetWindowLongW( hwnd, 0, wParam );
3133         break;
3134
3135     case MM_GETMENUHANDLE:
3136         return GetWindowLongW( hwnd, 0 );
3137
3138     default:
3139         return DefWindowProcW( hwnd, message, wParam, lParam );
3140     }
3141     return 0;
3142 }
3143
3144
3145 /***********************************************************************
3146  *           MENU_GetMenuBarHeight
3147  *
3148  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3149  */
3150 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3151                               INT orgX, INT orgY )
3152 {
3153     HDC hdc;
3154     RECT rectBar;
3155     LPPOPUPMENU lppop;
3156
3157     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3158
3159     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3160
3161     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3162     SelectObject( hdc, hMenuFont);
3163     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3164     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3165     ReleaseDC( hwnd, hdc );
3166     return lppop->Height;
3167 }
3168
3169
3170 /*******************************************************************
3171  *         ChangeMenuA    (USER32.@)
3172  */
3173 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3174                              UINT id, UINT flags )
3175 {
3176     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3177     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3178                                                  id, data );
3179     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3180     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3181                                                 id, data );
3182     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3183                                               flags & MF_BYPOSITION ? pos : id,
3184                                               flags & ~MF_REMOVE );
3185     /* Default: MF_INSERT */
3186     return InsertMenuA( hMenu, pos, flags, id, data );
3187 }
3188
3189
3190 /*******************************************************************
3191  *         ChangeMenuW    (USER32.@)
3192  */
3193 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3194                              UINT id, UINT flags )
3195 {
3196     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3197     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3198                                                  id, data );
3199     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3200     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3201                                                 id, data );
3202     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3203                                               flags & MF_BYPOSITION ? pos : id,
3204                                               flags & ~MF_REMOVE );
3205     /* Default: MF_INSERT */
3206     return InsertMenuW( hMenu, pos, flags, id, data );
3207 }
3208
3209
3210 /*******************************************************************
3211  *         CheckMenuItem    (USER32.@)
3212  */
3213 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3214 {
3215     MENUITEM *item;
3216     DWORD ret;
3217
3218     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3219     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3220     ret = item->fState & MF_CHECKED;
3221     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3222     else item->fState &= ~MF_CHECKED;
3223     return ret;
3224 }
3225
3226
3227 /**********************************************************************
3228  *         EnableMenuItem    (USER32.@)
3229  */
3230 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3231 {
3232     UINT    oldflags;
3233     MENUITEM *item;
3234     POPUPMENU *menu;
3235
3236     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3237
3238     /* Get the Popupmenu to access the owner menu */
3239     if (!(menu = MENU_GetMenu(hMenu)))
3240         return (UINT)-1;
3241
3242     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3243         return (UINT)-1;
3244
3245     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3246     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3247
3248     /* If the close item in the system menu change update the close button */
3249     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3250     {
3251         if (menu->hSysMenuOwner != 0)
3252         {
3253             POPUPMENU* parentMenu;
3254
3255             /* Get the parent menu to access*/
3256             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3257                 return (UINT)-1;
3258
3259             /* Refresh the frame to reflect the change*/
3260             SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3261                          SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3262         }
3263     }
3264
3265     return oldflags;
3266 }
3267
3268
3269 /*******************************************************************
3270  *         GetMenuStringA    (USER32.@)
3271  */
3272 INT WINAPI GetMenuStringA(
3273         HMENU hMenu,    /* [in] menuhandle */
3274         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3275         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3276         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3277         UINT wFlags     /* [in] MF_ flags */
3278 ) {
3279     MENUITEM *item;
3280
3281     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3282     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3283     if (!IS_STRING_ITEM(item->fType)) return 0;
3284     if (!str || !nMaxSiz) return strlenW(item->text);
3285     str[0] = '\0';
3286     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3287         str[nMaxSiz-1] = 0;
3288     TRACE("returning '%s'\n", str );
3289     return strlen(str);
3290 }
3291
3292
3293 /*******************************************************************
3294  *         GetMenuStringW    (USER32.@)
3295  */
3296 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3297                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3298 {
3299     MENUITEM *item;
3300
3301     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3302     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3303     if (!IS_STRING_ITEM(item->fType)) return 0;
3304     if (!str || !nMaxSiz) return strlenW(item->text);
3305     str[0] = '\0';
3306     lstrcpynW( str, item->text, nMaxSiz );
3307     return strlenW(str);
3308 }
3309
3310
3311 /**********************************************************************
3312  *         HiliteMenuItem    (USER32.@)
3313  */
3314 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3315                                 UINT wHilite )
3316 {
3317     LPPOPUPMENU menu;
3318     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3319     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3320     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3321     if (menu->FocusedItem == wItemID) return TRUE;
3322     MENU_HideSubPopups( hWnd, hMenu, FALSE );
3323     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3324     return TRUE;
3325 }
3326
3327
3328 /**********************************************************************
3329  *         GetMenuState    (USER32.@)
3330  */
3331 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3332 {
3333     MENUITEM *item;
3334     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3335     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3336     debug_print_menuitem ("  item: ", item, "");
3337     if (item->fType & MF_POPUP)
3338     {
3339         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3340         if (!menu) return -1;
3341         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3342     }
3343     else
3344     {
3345         /* We used to (from way back then) mask the result to 0xff.  */
3346         /* I don't know why and it seems wrong as the documented */
3347         /* return flag MF_SEPARATOR is outside that mask.  */
3348         return (item->fType | item->fState);
3349     }
3350 }
3351
3352
3353 /**********************************************************************
3354  *         GetMenuItemCount    (USER32.@)
3355  */
3356 INT WINAPI GetMenuItemCount( HMENU hMenu )
3357 {
3358     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3359     if (!menu) return -1;
3360     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3361     return menu->nItems;
3362 }
3363
3364
3365 /**********************************************************************
3366  *         GetMenuItemID    (USER32.@)
3367  */
3368 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3369 {
3370     MENUITEM * lpmi;
3371
3372     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3373     if (lpmi->fType & MF_POPUP) return -1;
3374     return lpmi->wID;
3375
3376 }
3377
3378
3379 /*******************************************************************
3380  *         InsertMenuW    (USER32.@)
3381  */
3382 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3383                          UINT_PTR id, LPCWSTR str )
3384 {
3385     MENUITEM *item;
3386
3387     if (IS_STRING_ITEM(flags) && str)
3388         TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3389               hMenu, pos, flags, id, debugstr_w(str) );
3390     else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3391                hMenu, pos, flags, id, (DWORD)str );
3392
3393     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3394
3395     if (!(MENU_SetItemData( item, flags, id, str )))
3396     {
3397         RemoveMenu( hMenu, pos, flags );
3398         return FALSE;
3399     }
3400
3401     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3402         (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3403
3404     item->hCheckBit = item->hUnCheckBit = 0;
3405     return TRUE;
3406 }
3407
3408
3409 /*******************************************************************
3410  *         InsertMenuA    (USER32.@)
3411  */
3412 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3413                          UINT_PTR id, LPCSTR str )
3414 {
3415     BOOL ret = FALSE;
3416
3417     if (IS_STRING_ITEM(flags) && str)
3418     {
3419         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3420         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3421         if (newstr)
3422         {
3423             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3424             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3425             HeapFree( GetProcessHeap(), 0, newstr );
3426         }
3427         return ret;
3428     }
3429     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3430 }
3431
3432
3433 /*******************************************************************
3434  *         AppendMenuA    (USER32.@)
3435  */
3436 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3437                          UINT_PTR id, LPCSTR data )
3438 {
3439     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3440 }
3441
3442
3443 /*******************************************************************
3444  *         AppendMenuW    (USER32.@)
3445  */
3446 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3447                          UINT_PTR id, LPCWSTR data )
3448 {
3449     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3450 }
3451
3452
3453 /**********************************************************************
3454  *         RemoveMenu    (USER32.@)
3455  */
3456 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3457 {
3458     LPPOPUPMENU menu;
3459     MENUITEM *item;
3460
3461     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3462     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3463     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3464
3465       /* Remove item */
3466
3467     MENU_FreeItemData( item );
3468
3469     if (--menu->nItems == 0)
3470     {
3471         HeapFree( GetProcessHeap(), 0, menu->items );
3472         menu->items = NULL;
3473     }
3474     else
3475     {
3476         while(nPos < menu->nItems)
3477         {
3478             *item = *(item+1);
3479             item++;
3480             nPos++;
3481         }
3482         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3483                                    menu->nItems * sizeof(MENUITEM) );
3484     }
3485     return TRUE;
3486 }
3487
3488
3489 /**********************************************************************
3490  *         DeleteMenu    (USER32.@)
3491  */
3492 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3493 {
3494     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3495     if (!item) return FALSE;
3496     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3497       /* nPos is now the position of the item */
3498     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3499     return TRUE;
3500 }
3501
3502
3503 /*******************************************************************
3504  *         ModifyMenuW    (USER32.@)
3505  */
3506 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3507                          UINT_PTR id, LPCWSTR str )
3508 {
3509     MENUITEM *item;
3510
3511     if (IS_STRING_ITEM(flags))
3512     {
3513         TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3514     }
3515     else
3516     {
3517         TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3518     }
3519
3520     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3521     return MENU_SetItemData( item, flags, id, str );
3522 }
3523
3524
3525 /*******************************************************************
3526  *         ModifyMenuA    (USER32.@)
3527  */
3528 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3529                          UINT_PTR id, LPCSTR str )
3530 {
3531     BOOL ret = FALSE;
3532
3533     if (IS_STRING_ITEM(flags) && str)
3534     {
3535         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3536         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3537         if (newstr)
3538         {
3539             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3540             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3541             HeapFree( GetProcessHeap(), 0, newstr );
3542         }
3543         return ret;
3544     }
3545     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3546 }
3547
3548
3549 /**********************************************************************
3550  *         CreatePopupMenu    (USER32.@)
3551  */
3552 HMENU WINAPI CreatePopupMenu(void)
3553 {
3554     HMENU hmenu;
3555     POPUPMENU *menu;
3556
3557     if (!(hmenu = CreateMenu())) return 0;
3558     menu = MENU_GetMenu( hmenu );
3559     menu->wFlags |= MF_POPUP;
3560     menu->bTimeToHide = FALSE;
3561     return hmenu;
3562 }
3563
3564
3565 /**********************************************************************
3566  *         GetMenuCheckMarkDimensions    (USER.417)
3567  *         GetMenuCheckMarkDimensions    (USER32.@)
3568  */
3569 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3570 {
3571     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3572 }
3573
3574
3575 /**********************************************************************
3576  *         SetMenuItemBitmaps    (USER32.@)
3577  */
3578 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3579                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3580 {
3581     MENUITEM *item;
3582     TRACE("(%p, %04x, %04x, %p, %p)\n",
3583           hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3584     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3585
3586     if (!hNewCheck && !hNewUnCheck)
3587     {
3588         item->fState &= ~MF_USECHECKBITMAPS;
3589     }
3590     else  /* Install new bitmaps */
3591     {
3592         item->hCheckBit = hNewCheck;
3593         item->hUnCheckBit = hNewUnCheck;
3594         item->fState |= MF_USECHECKBITMAPS;
3595     }
3596     return TRUE;
3597 }
3598
3599
3600 /**********************************************************************
3601  *         CreateMenu    (USER32.@)
3602  */
3603 HMENU WINAPI CreateMenu(void)
3604 {
3605     HMENU hMenu;
3606     LPPOPUPMENU menu;
3607     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3608     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3609
3610     ZeroMemory(menu, sizeof(POPUPMENU));
3611     menu->wMagic = MENU_MAGIC;
3612     menu->FocusedItem = NO_SELECTED_ITEM;
3613     menu->bTimeToHide = FALSE;
3614
3615     TRACE("return %p\n", hMenu );
3616
3617     return hMenu;
3618 }
3619
3620
3621 /**********************************************************************
3622  *         DestroyMenu    (USER32.@)
3623  */
3624 BOOL WINAPI DestroyMenu( HMENU hMenu )
3625 {
3626     TRACE("(%p)\n", hMenu);
3627
3628     /* Silently ignore attempts to destroy default system popup */
3629
3630     if (hMenu && hMenu != MENU_DefSysPopup)
3631     {
3632         LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3633
3634         if (!lppop) return FALSE;
3635
3636         lppop->wMagic = 0;  /* Mark it as destroyed */
3637
3638         /* DestroyMenu should not destroy system menu popup owner */
3639         if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3640         {
3641             DestroyWindow( lppop->hWnd );
3642             lppop->hWnd = 0;
3643         }
3644
3645         if (lppop->items) /* recursively destroy submenus */
3646         {
3647             int i;
3648             MENUITEM *item = lppop->items;
3649             for (i = lppop->nItems; i > 0; i--, item++)
3650             {
3651                 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3652                 MENU_FreeItemData( item );
3653             }
3654             HeapFree( GetProcessHeap(), 0, lppop->items );
3655         }
3656         USER_HEAP_FREE( hMenu );
3657     }
3658     return (hMenu != MENU_DefSysPopup);
3659 }
3660
3661
3662 /**********************************************************************
3663  *         GetSystemMenu    (USER32.@)
3664  */
3665 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3666 {
3667     WND *wndPtr = WIN_GetPtr( hWnd );
3668     HMENU retvalue = 0;
3669
3670     if (wndPtr == WND_OTHER_PROCESS)
3671     {
3672         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3673     }
3674     else if (wndPtr)
3675     {
3676         if( wndPtr->hSysMenu )
3677         {
3678             if( bRevert )
3679             {
3680                 DestroyMenu(wndPtr->hSysMenu);
3681                 wndPtr->hSysMenu = 0;
3682             }
3683             else
3684             {
3685                 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3686                 if( menu )
3687                 {
3688                    if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3689                       menu->items[0].hSubMenu = MENU_CopySysPopup();
3690                 }
3691                 else
3692                 {
3693                    WARN("Current sys-menu (%p) of wnd %p is broken\n",
3694                         wndPtr->hSysMenu, hWnd);
3695                    wndPtr->hSysMenu = 0;
3696                 }
3697             }
3698         }
3699
3700         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3701             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3702
3703         if( wndPtr->hSysMenu )
3704         {
3705             POPUPMENU *menu;
3706             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3707
3708             /* Store the dummy sysmenu handle to facilitate the refresh */
3709             /* of the close button if the SC_CLOSE item change */
3710             menu = MENU_GetMenu(retvalue);
3711             if ( menu )
3712                menu->hSysMenuOwner = wndPtr->hSysMenu;
3713         }
3714         WIN_ReleasePtr( wndPtr );
3715     }
3716     return bRevert ? 0 : retvalue;
3717 }
3718
3719
3720 /*******************************************************************
3721  *         SetSystemMenu    (USER32.@)
3722  */
3723 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3724 {
3725     WND *wndPtr = WIN_GetPtr( hwnd );
3726
3727     if (wndPtr && wndPtr != WND_OTHER_PROCESS)
3728     {
3729         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3730         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3731         WIN_ReleasePtr( wndPtr );
3732         return TRUE;
3733     }
3734     return FALSE;
3735 }
3736
3737
3738 /**********************************************************************
3739  *         GetMenu    (USER32.@)
3740  */
3741 HMENU WINAPI GetMenu( HWND hWnd )
3742 {
3743     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3744     TRACE("for %p returning %p\n", hWnd, retvalue);
3745     return retvalue;
3746 }
3747
3748
3749 /**********************************************************************
3750  *         MENU_SetMenu
3751  *
3752  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3753  * SetWindowPos call that would result if SetMenu were called directly.
3754  */
3755 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3756 {
3757     TRACE("(%p, %p);\n", hWnd, hMenu);
3758
3759     if (hMenu && !IsMenu(hMenu))
3760     {
3761         WARN("hMenu %p is not a menu handle\n", hMenu);
3762         return FALSE;
3763     }
3764     if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3765
3766     hWnd = WIN_GetFullHandle( hWnd );
3767     if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
3768
3769     if (hMenu != 0)
3770     {
3771         LPPOPUPMENU lpmenu;
3772
3773         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3774
3775         lpmenu->hWnd = hWnd;
3776         lpmenu->Height = 0;  /* Make sure we recalculate the size */
3777     }
3778     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3779     return TRUE;
3780 }
3781
3782
3783 /**********************************************************************
3784  *         SetMenu    (USER32.@)
3785  */
3786 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3787 {   
3788     if(!MENU_SetMenu(hWnd, hMenu))
3789         return FALSE;
3790  
3791     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3792                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3793     return TRUE;
3794 }
3795
3796
3797 /**********************************************************************
3798  *         GetSubMenu    (USER32.@)
3799  */
3800 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3801 {
3802     MENUITEM * lpmi;
3803
3804     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3805     if (!(lpmi->fType & MF_POPUP)) return 0;
3806     return lpmi->hSubMenu;
3807 }
3808
3809
3810 /**********************************************************************
3811  *         DrawMenuBar    (USER32.@)
3812  */
3813 BOOL WINAPI DrawMenuBar( HWND hWnd )
3814 {
3815     LPPOPUPMENU lppop;
3816     HMENU hMenu = GetMenu(hWnd);
3817
3818     if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3819     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3820
3821     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3822     lppop->hwndOwner = hWnd;
3823     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3824                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3825     return TRUE;
3826 }
3827
3828 /***********************************************************************
3829  *           DrawMenuBarTemp   (USER32.@)
3830  *
3831  * UNDOCUMENTED !!
3832  *
3833  * called by W98SE desk.cpl Control Panel Applet
3834  *
3835  * Not 100% sure about the param names, but close.
3836  */
3837 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3838 {
3839     LPPOPUPMENU lppop;
3840     UINT i,retvalue;
3841     HFONT hfontOld = 0;
3842
3843     if (!hMenu)
3844         hMenu = GetMenu(hwnd);
3845
3846     if (!hFont)
3847         hFont = hMenuFont;
3848
3849     lppop = MENU_GetMenu( hMenu );
3850     if (lppop == NULL || lprect == NULL)
3851     {
3852         retvalue = GetSystemMetrics(SM_CYMENU);
3853         goto END;
3854     }
3855
3856     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3857
3858     hfontOld = SelectObject( hDC, hFont);
3859
3860     if (lppop->Height == 0)
3861         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3862
3863     lprect->bottom = lprect->top + lppop->Height;
3864
3865     FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3866
3867     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3868     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3869     LineTo( hDC, lprect->right, lprect->bottom );
3870
3871     if (lppop->nItems == 0)
3872     {
3873         retvalue = GetSystemMetrics(SM_CYMENU);
3874         goto END;
3875     }
3876
3877     for (i = 0; i < lppop->nItems; i++)
3878     {
3879         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3880                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3881     }
3882     retvalue = lppop->Height;
3883
3884 END:
3885     if (hfontOld) SelectObject (hDC, hfontOld);
3886     return retvalue;
3887 }
3888
3889 /***********************************************************************
3890  *           EndMenu   (USER.187)
3891  *           EndMenu   (USER32.@)
3892  */
3893 void WINAPI EndMenu(void)
3894 {
3895     /* if we are in the menu code, and it is active */
3896     if (!fEndMenu && top_popup)
3897     {
3898         /* terminate the menu handling code */
3899         fEndMenu = TRUE;
3900
3901         /* needs to be posted to wakeup the internal menu handler */
3902         /* which will now terminate the menu, in the event that */
3903         /* the main window was minimized, or lost focus, so we */
3904         /* don't end up with an orphaned menu */
3905         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3906     }
3907 }
3908
3909
3910 /***********************************************************************
3911  *           LookupMenuHandle   (USER.217)
3912  */
3913 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3914 {
3915     HMENU hmenu32 = HMENU_32(hmenu);
3916     UINT id32 = id;
3917     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3918     else return HMENU_16(hmenu32);
3919 }
3920
3921
3922 /**********************************************************************
3923  *          LoadMenu    (USER.150)
3924  */
3925 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3926 {
3927     HRSRC16 hRsrc;
3928     HGLOBAL16 handle;
3929     HMENU16 hMenu;
3930
3931     if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3932     if (!name) return 0;
3933
3934     instance = GetExePtr( instance );
3935     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3936     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3937     hMenu = LoadMenuIndirect16(LockResource16(handle));
3938     FreeResource16( handle );
3939     return hMenu;
3940 }
3941
3942
3943 /*****************************************************************
3944  *        LoadMenuA   (USER32.@)
3945  */
3946 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3947 {
3948     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3949     if (!hrsrc) return 0;
3950     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3951 }
3952
3953
3954 /*****************************************************************
3955  *        LoadMenuW   (USER32.@)
3956  */
3957 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3958 {
3959     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3960     if (!hrsrc) return 0;
3961     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3962 }
3963
3964
3965 /**********************************************************************
3966  *          LoadMenuIndirect    (USER.220)
3967  */
3968 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3969 {
3970     HMENU hMenu;
3971     WORD version, offset;
3972     LPCSTR p = (LPCSTR)template;
3973
3974     TRACE("(%p)\n", template );
3975     version = GET_WORD(p);
3976     p += sizeof(WORD);
3977     if (version)
3978     {
3979         WARN("version must be 0 for Win16\n" );
3980         return 0;
3981     }
3982     offset = GET_WORD(p);
3983     p += sizeof(WORD) + offset;
3984     if (!(hMenu = CreateMenu())) return 0;
3985     if (!MENU_ParseResource( p, hMenu, FALSE ))
3986     {
3987         DestroyMenu( hMenu );
3988         return 0;
3989     }
3990     return HMENU_16(hMenu);
3991 }
3992
3993
3994 /**********************************************************************
3995  *          LoadMenuIndirectW    (USER32.@)
3996  */
3997 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
3998 {
3999     HMENU hMenu;
4000     WORD version, offset;
4001     LPCSTR p = (LPCSTR)template;
4002
4003     version = GET_WORD(p);
4004     p += sizeof(WORD);
4005     TRACE("%p, ver %d\n", template, version );
4006     switch (version)
4007     {
4008       case 0: /* standard format is version of 0 */
4009         offset = GET_WORD(p);
4010         p += sizeof(WORD) + offset;
4011         if (!(hMenu = CreateMenu())) return 0;
4012         if (!MENU_ParseResource( p, hMenu, TRUE ))
4013           {
4014             DestroyMenu( hMenu );
4015             return 0;
4016           }
4017         return hMenu;
4018       case 1: /* extended format is version of 1 */
4019         offset = GET_WORD(p);
4020         p += sizeof(WORD) + offset;
4021         if (!(hMenu = CreateMenu())) return 0;
4022         if (!MENUEX_ParseResource( p, hMenu))
4023           {
4024             DestroyMenu( hMenu );
4025             return 0;
4026           }
4027         return hMenu;
4028       default:
4029         ERR("version %d not supported.\n", version);
4030         return 0;
4031     }
4032 }
4033
4034
4035 /**********************************************************************
4036  *          LoadMenuIndirectA    (USER32.@)
4037  */
4038 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4039 {
4040     return LoadMenuIndirectW( template );
4041 }
4042
4043
4044 /**********************************************************************
4045  *              IsMenu    (USER32.@)
4046  */
4047 BOOL WINAPI IsMenu(HMENU hmenu)
4048 {
4049     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4050     return menu != NULL;
4051 }
4052
4053 /**********************************************************************
4054  *              GetMenuItemInfo_common
4055  */
4056
4057 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4058                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4059 {
4060     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4061
4062     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4063
4064     if (!menu)
4065         return FALSE;
4066
4067     if (lpmii->fMask & MIIM_TYPE) {
4068         lpmii->fType = menu->fType;
4069         switch (MENU_ITEM_TYPE(menu->fType)) {
4070         case MF_STRING:
4071             break;  /* will be done below */
4072         case MF_OWNERDRAW:
4073         case MF_BITMAP:
4074             lpmii->dwTypeData = menu->text;
4075             /* fall through */
4076         default:
4077             lpmii->cch = 0;
4078         }
4079     }
4080
4081     /* copy the text string */
4082     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4083          (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4084     {
4085         int len;
4086         if (unicode)
4087         {
4088             len = strlenW(menu->text);
4089             if(lpmii->dwTypeData && lpmii->cch)
4090                 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4091         }
4092         else
4093         {
4094             len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4095             if(lpmii->dwTypeData && lpmii->cch)
4096                 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4097                                           (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4098                     ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4099         }
4100         /* if we've copied a substring we return its length */
4101         if(lpmii->dwTypeData && lpmii->cch)
4102         {
4103             if (lpmii->cch <= len) lpmii->cch--;
4104         }
4105         else /* return length of string */
4106             lpmii->cch = len;
4107     }
4108
4109     if (lpmii->fMask & MIIM_FTYPE)
4110         lpmii->fType = menu->fType;
4111
4112     if (lpmii->fMask & MIIM_BITMAP)
4113         lpmii->hbmpItem = menu->hbmpItem;
4114
4115     if (lpmii->fMask & MIIM_STATE)
4116         lpmii->fState = menu->fState;
4117
4118     if (lpmii->fMask & MIIM_ID)
4119         lpmii->wID = menu->wID;
4120
4121     if (lpmii->fMask & MIIM_SUBMENU)
4122         lpmii->hSubMenu = menu->hSubMenu;
4123
4124     if (lpmii->fMask & MIIM_CHECKMARKS) {
4125         lpmii->hbmpChecked = menu->hCheckBit;
4126         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4127     }
4128     if (lpmii->fMask & MIIM_DATA)
4129         lpmii->dwItemData = menu->dwItemData;
4130
4131   return TRUE;
4132 }
4133
4134 /**********************************************************************
4135  *              GetMenuItemInfoA    (USER32.@)
4136  */
4137 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4138                                   LPMENUITEMINFOA lpmii)
4139 {
4140     return GetMenuItemInfo_common (hmenu, item, bypos,
4141                                     (LPMENUITEMINFOW)lpmii, FALSE);
4142 }
4143
4144 /**********************************************************************
4145  *              GetMenuItemInfoW    (USER32.@)
4146  */
4147 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4148                                   LPMENUITEMINFOW lpmii)
4149 {
4150     return GetMenuItemInfo_common (hmenu, item, bypos,
4151                                      lpmii, TRUE);
4152 }
4153
4154
4155 /* set a menu item text from a ASCII or Unicode string */
4156 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4157 {
4158     if (!text)
4159     {
4160         menu->text = NULL;
4161         menu->fType |= MF_SEPARATOR;
4162     }
4163     else if (unicode)
4164     {
4165         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4166             strcpyW( menu->text, text );
4167     }
4168     else
4169     {
4170         LPCSTR str = (LPCSTR)text;
4171         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4172         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4173             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4174     }
4175 }
4176
4177
4178 /**********************************************************************
4179  *              SetMenuItemInfo_common
4180  */
4181
4182 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4183                                        const MENUITEMINFOW *lpmii,
4184                                        BOOL unicode)
4185 {
4186     if (!menu) return FALSE;
4187
4188     debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4189
4190     if (lpmii->fMask & MIIM_TYPE ) {
4191         /* Get rid of old string. */
4192         if (IS_STRING_ITEM(menu->fType) && menu->text) {
4193             HeapFree(GetProcessHeap(), 0, menu->text);
4194             menu->text = NULL;
4195         }
4196
4197         /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4198         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4199         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4200
4201         menu->text = lpmii->dwTypeData;
4202
4203        if (IS_STRING_ITEM(menu->fType))
4204            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4205     }
4206
4207     if (lpmii->fMask & MIIM_FTYPE ) {
4208         /* free the string when the type is changing */
4209         if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4210             HeapFree(GetProcessHeap(), 0, menu->text);
4211             menu->text = NULL;
4212         }
4213         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4214         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4215         if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4216             menu->fType |= MF_SEPARATOR;
4217     }
4218
4219     if (lpmii->fMask & MIIM_STRING ) {
4220         if (IS_STRING_ITEM(menu->fType)) {
4221             /* free the string when used */
4222             HeapFree(GetProcessHeap(), 0, menu->text);
4223             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4224         }
4225     }
4226
4227     if (lpmii->fMask & MIIM_STATE)
4228     {
4229         /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4230         menu->fState = lpmii->fState;
4231     }
4232
4233     if (lpmii->fMask & MIIM_ID)
4234         menu->wID = lpmii->wID;
4235
4236     if (lpmii->fMask & MIIM_SUBMENU) {
4237         menu->hSubMenu = lpmii->hSubMenu;
4238         if (menu->hSubMenu) {
4239             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4240             if (subMenu) {
4241                 subMenu->wFlags |= MF_POPUP;
4242                 menu->fType |= MF_POPUP;
4243             }
4244             else
4245                 /* FIXME: Return an error ? */
4246                 menu->fType &= ~MF_POPUP;
4247         }
4248         else
4249             menu->fType &= ~MF_POPUP;
4250     }
4251
4252     if (lpmii->fMask & MIIM_CHECKMARKS)
4253     {
4254         if (lpmii->fType & MFT_RADIOCHECK)
4255             menu->fType |= MFT_RADIOCHECK;
4256
4257         menu->hCheckBit = lpmii->hbmpChecked;
4258         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4259     }
4260     if (lpmii->fMask & MIIM_DATA)
4261         menu->dwItemData = lpmii->dwItemData;
4262
4263     if (lpmii->fMask & MIIM_BITMAP)
4264         menu->hbmpItem = lpmii->hbmpItem;
4265
4266     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4267     return TRUE;
4268 }
4269
4270 /**********************************************************************
4271  *              SetMenuItemInfoA    (USER32.@)
4272  */
4273 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4274                                  const MENUITEMINFOA *lpmii)
4275 {
4276     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4277                                     (const MENUITEMINFOW *)lpmii, FALSE);
4278 }
4279
4280 /**********************************************************************
4281  *              SetMenuItemInfoW    (USER32.@)
4282  */
4283 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4284                                  const MENUITEMINFOW *lpmii)
4285 {
4286     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4287                                     lpmii, TRUE);
4288 }
4289
4290 /**********************************************************************
4291  *              SetMenuDefaultItem    (USER32.@)
4292  *
4293  */
4294 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4295 {
4296         UINT i;
4297         POPUPMENU *menu;
4298         MENUITEM *item;
4299
4300         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4301
4302         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4303
4304         /* reset all default-item flags */
4305         item = menu->items;
4306         for (i = 0; i < menu->nItems; i++, item++)
4307         {
4308             item->fState &= ~MFS_DEFAULT;
4309         }
4310
4311         /* no default item */
4312         if ( -1 == uItem)
4313         {
4314             return TRUE;
4315         }
4316
4317         item = menu->items;
4318         if ( bypos )
4319         {
4320             if ( uItem >= menu->nItems ) return FALSE;
4321             item[uItem].fState |= MFS_DEFAULT;
4322             return TRUE;
4323         }
4324         else
4325         {
4326             for (i = 0; i < menu->nItems; i++, item++)
4327             {
4328                 if (item->wID == uItem)
4329                 {
4330                      item->fState |= MFS_DEFAULT;
4331                      return TRUE;
4332                 }
4333             }
4334
4335         }
4336         return FALSE;
4337 }
4338
4339 /**********************************************************************
4340  *              GetMenuDefaultItem    (USER32.@)
4341  */
4342 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4343 {
4344         POPUPMENU *menu;
4345         MENUITEM * item;
4346         UINT i = 0;
4347
4348         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4349
4350         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4351
4352         /* find default item */
4353         item = menu->items;
4354
4355         /* empty menu */
4356         if (! item) return -1;
4357
4358         while ( !( item->fState & MFS_DEFAULT ) )
4359         {
4360             i++; item++;
4361             if  (i >= menu->nItems ) return -1;
4362         }
4363
4364         /* default: don't return disabled items */
4365         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4366
4367         /* search rekursiv when needed */
4368         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4369         {
4370             UINT ret;
4371             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4372             if ( -1 != ret ) return ret;
4373
4374             /* when item not found in submenu, return the popup item */
4375         }
4376         return ( bypos ) ? i : item->wID;
4377
4378 }
4379
4380
4381 /**********************************************************************
4382  *              InsertMenuItemA    (USER32.@)
4383  */
4384 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4385                                 const MENUITEMINFOA *lpmii)
4386 {
4387     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4388     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4389 }
4390
4391
4392 /**********************************************************************
4393  *              InsertMenuItemW    (USER32.@)
4394  */
4395 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4396                                 const MENUITEMINFOW *lpmii)
4397 {
4398     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4399     return SetMenuItemInfo_common(item, lpmii, TRUE);
4400 }
4401
4402 /**********************************************************************
4403  *              CheckMenuRadioItem    (USER32.@)
4404  */
4405
4406 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4407                                    UINT first, UINT last, UINT check,
4408                                    UINT bypos)
4409 {
4410      MENUITEM *mifirst, *milast, *micheck;
4411      HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4412
4413      TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4414
4415      mifirst = MENU_FindItem (&mfirst, &first, bypos);
4416      milast = MENU_FindItem (&mlast, &last, bypos);
4417      micheck = MENU_FindItem (&mcheck, &check, bypos);
4418
4419      if (mifirst == NULL || milast == NULL || micheck == NULL ||
4420          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4421          micheck > milast || micheck < mifirst)
4422           return FALSE;
4423
4424      while (mifirst <= milast)
4425      {
4426           if (mifirst == micheck)
4427           {
4428                mifirst->fType |= MFT_RADIOCHECK;
4429                mifirst->fState |= MFS_CHECKED;
4430           } else {
4431                mifirst->fType &= ~MFT_RADIOCHECK;
4432                mifirst->fState &= ~MFS_CHECKED;
4433           }
4434           mifirst++;
4435      }
4436
4437      return TRUE;
4438 }
4439
4440
4441 /**********************************************************************
4442  *              GetMenuItemRect    (USER32.@)
4443  *
4444  *      ATTENTION: Here, the returned values in rect are the screen
4445  *                 coordinates of the item just like if the menu was
4446  *                 always on the upper left side of the application.
4447  *
4448  */
4449 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4450                                  LPRECT rect)
4451 {
4452      POPUPMENU *itemMenu;
4453      MENUITEM *item;
4454      HWND referenceHwnd;
4455
4456      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4457
4458      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4459      referenceHwnd = hwnd;
4460
4461      if(!hwnd)
4462      {
4463          itemMenu = MENU_GetMenu(hMenu);
4464          if (itemMenu == NULL)
4465              return FALSE;
4466
4467          if(itemMenu->hWnd == 0)
4468              return FALSE;
4469          referenceHwnd = itemMenu->hWnd;
4470      }
4471
4472      if ((rect == NULL) || (item == NULL))
4473          return FALSE;
4474
4475      *rect = item->rect;
4476
4477      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4478
4479      return TRUE;
4480 }
4481
4482
4483 /**********************************************************************
4484  *              SetMenuInfo    (USER32.@)
4485  *
4486  * FIXME
4487  *      MIM_APPLYTOSUBMENUS
4488  *      actually use the items to draw the menu
4489  */
4490 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4491 {
4492     POPUPMENU *menu;
4493
4494     TRACE("(%p %p)\n", hMenu, lpmi);
4495
4496     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4497     {
4498
4499         if (lpmi->fMask & MIM_BACKGROUND)
4500             menu->hbrBack = lpmi->hbrBack;
4501
4502         if (lpmi->fMask & MIM_HELPID)
4503             menu->dwContextHelpID = lpmi->dwContextHelpID;
4504
4505         if (lpmi->fMask & MIM_MAXHEIGHT)
4506             menu->cyMax = lpmi->cyMax;
4507
4508         if (lpmi->fMask & MIM_MENUDATA)
4509             menu->dwMenuData = lpmi->dwMenuData;
4510
4511         if (lpmi->fMask & MIM_STYLE)
4512         {
4513             menu->dwStyle = lpmi->dwStyle;
4514             if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4515             if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4516             if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4517             if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4518             if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4519         }
4520
4521         return TRUE;
4522     }
4523     return FALSE;
4524 }
4525
4526 /**********************************************************************
4527  *              GetMenuInfo    (USER32.@)
4528  *
4529  *  NOTES
4530  *      win98/NT5.0
4531  *
4532  */
4533 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4534 {   POPUPMENU *menu;
4535
4536     TRACE("(%p %p)\n", hMenu, lpmi);
4537
4538     if (lpmi && (menu = MENU_GetMenu(hMenu)))
4539     {
4540
4541         if (lpmi->fMask & MIM_BACKGROUND)
4542             lpmi->hbrBack = menu->hbrBack;
4543
4544         if (lpmi->fMask & MIM_HELPID)
4545             lpmi->dwContextHelpID = menu->dwContextHelpID;
4546
4547         if (lpmi->fMask & MIM_MAXHEIGHT)
4548             lpmi->cyMax = menu->cyMax;
4549
4550         if (lpmi->fMask & MIM_MENUDATA)
4551             lpmi->dwMenuData = menu->dwMenuData;
4552
4553         if (lpmi->fMask & MIM_STYLE)
4554             lpmi->dwStyle = menu->dwStyle;
4555
4556         return TRUE;
4557     }
4558     return FALSE;
4559 }
4560
4561
4562 /**********************************************************************
4563  *         SetMenuContextHelpId    (USER32.@)
4564  */
4565 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4566 {
4567     LPPOPUPMENU menu;
4568
4569     TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4570
4571     if ((menu = MENU_GetMenu(hMenu)))
4572     {
4573         menu->dwContextHelpID = dwContextHelpID;
4574         return TRUE;
4575     }
4576     return FALSE;
4577 }
4578
4579
4580 /**********************************************************************
4581  *         GetMenuContextHelpId    (USER32.@)
4582  */
4583 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4584 {
4585     LPPOPUPMENU menu;
4586
4587     TRACE("(%p)\n", hMenu);
4588
4589     if ((menu = MENU_GetMenu(hMenu)))
4590     {
4591         return menu->dwContextHelpID;
4592     }
4593     return 0;
4594 }
4595
4596 /**********************************************************************
4597  *         MenuItemFromPoint    (USER32.@)
4598  */
4599 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4600 {
4601     POPUPMENU *menu = MENU_GetMenu(hMenu);
4602     UINT pos;
4603
4604     /*FIXME: Do we have to handle hWnd here? */
4605     if (!menu) return -1;
4606     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4607     return pos;
4608 }
4609
4610
4611 /**********************************************************************
4612  *           translate_accelerator
4613  */
4614 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4615                                    BYTE fVirt, WORD key, WORD cmd )
4616 {
4617     INT mask = 0;
4618     UINT mesg = 0;
4619
4620     if (wParam != key) return FALSE;
4621
4622     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4623     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4624     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4625
4626     if (message == WM_CHAR || message == WM_SYSCHAR)
4627     {
4628         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4629         {
4630             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4631             goto found;
4632         }
4633     }
4634     else
4635     {
4636         if(fVirt & FVIRTKEY)
4637         {
4638             TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4639                           wParam, 0xff & HIWORD(lParam));
4640
4641             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4642             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4643         }
4644         else
4645         {
4646             if (!(lParam & 0x01000000))  /* no special_key */
4647             {
4648                 if ((fVirt & FALT) && (lParam & 0x20000000))
4649                 {                              /* ^^ ALT pressed */
4650                     TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4651                     goto found;
4652                 }
4653             }
4654         }
4655     }
4656     return FALSE;
4657
4658  found:
4659     if (message == WM_KEYUP || message == WM_SYSKEYUP)
4660         mesg = 1;
4661     else
4662     {
4663         HMENU hMenu, hSubMenu, hSysMenu;
4664         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4665
4666         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4667         hSysMenu = get_win_sys_menu( hWnd );
4668
4669         /* find menu item and ask application to initialize it */
4670         /* 1. in the system menu */
4671         hSubMenu = hSysMenu;
4672         nPos = cmd;
4673         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4674         {
4675             if (GetCapture())
4676                 mesg = 2;
4677             if (!IsWindowEnabled(hWnd))
4678                 mesg = 3;
4679             else
4680             {
4681                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4682                 if(hSubMenu != hSysMenu)
4683                 {
4684                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4685                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4686                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4687                 }
4688                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4689             }
4690         }
4691         else /* 2. in the window's menu */
4692         {
4693             hSubMenu = hMenu;
4694             nPos = cmd;
4695             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4696             {
4697                 if (GetCapture())
4698                     mesg = 2;
4699                 if (!IsWindowEnabled(hWnd))
4700                     mesg = 3;
4701                 else
4702                 {
4703                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4704                     if(hSubMenu != hMenu)
4705                     {
4706                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4707                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4708                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4709                     }
4710                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4711                 }
4712             }
4713         }
4714
4715         if (mesg == 0)
4716         {
4717             if (uSysStat != (UINT)-1)
4718             {
4719                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4720                     mesg=4;
4721                 else
4722                     mesg=WM_SYSCOMMAND;
4723             }
4724             else
4725             {
4726                 if (uStat != (UINT)-1)
4727                 {
4728                     if (IsIconic(hWnd))
4729                         mesg=5;
4730                     else
4731                     {
4732                         if (uStat & (MF_DISABLED|MF_GRAYED))
4733                             mesg=6;
4734                         else
4735                             mesg=WM_COMMAND;
4736                     }
4737                 }
4738                 else
4739                     mesg=WM_COMMAND;
4740             }
4741         }
4742     }
4743
4744     if( mesg==WM_COMMAND )
4745     {
4746         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4747         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4748     }
4749     else if( mesg==WM_SYSCOMMAND )
4750     {
4751         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4752         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4753     }
4754     else
4755     {
4756         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
4757          *   #0: unknown (please report!)
4758          *   #1: for WM_KEYUP,WM_SYSKEYUP
4759          *   #2: mouse is captured
4760          *   #3: window is disabled
4761          *   #4: it's a disabled system menu option
4762          *   #5: it's a menu option, but window is iconic
4763          *   #6: it's a menu option, but disabled
4764          */
4765         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4766         if(mesg==0)
4767             ERR_(accel)(" unknown reason - please report!\n");
4768     }
4769     return TRUE;
4770 }
4771
4772 /**********************************************************************
4773  *      TranslateAccelerator      (USER32.@)
4774  *      TranslateAcceleratorA     (USER32.@)
4775  */
4776 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4777 {
4778     /* YES, Accel16! */
4779     LPACCEL16 lpAccelTbl;
4780     int i;
4781     WPARAM wParam;
4782
4783     if (!hWnd || !msg) return 0;
4784
4785     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4786     {
4787         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4788         return 0;
4789     }
4790
4791     wParam = msg->wParam;
4792
4793     switch (msg->message)
4794     {
4795     case WM_KEYDOWN:
4796     case WM_SYSKEYDOWN:
4797         break;
4798
4799     case WM_CHAR:
4800     case WM_SYSCHAR:
4801         {
4802             char ch = LOWORD(wParam);
4803             WCHAR wch;
4804             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4805             wParam = MAKEWPARAM(wch, HIWORD(wParam));
4806         }
4807         break;
4808
4809     default:
4810         return 0;
4811     }
4812
4813     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4814                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4815     i = 0;
4816     do
4817     {
4818         if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4819                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4820             return 1;
4821     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4822
4823     return 0;
4824 }
4825
4826 /**********************************************************************
4827  *      TranslateAcceleratorW     (USER32.@)
4828  */
4829 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4830 {
4831     /* YES, Accel16! */
4832     LPACCEL16 lpAccelTbl;
4833     int i;
4834
4835     if (!hWnd || !msg) return 0;
4836
4837     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4838     {
4839         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4840         return 0;
4841     }
4842
4843     switch (msg->message)
4844     {
4845     case WM_KEYDOWN:
4846     case WM_SYSKEYDOWN:
4847     case WM_CHAR:
4848     case WM_SYSCHAR:
4849         break;
4850
4851     default:
4852         return 0;
4853     }
4854
4855     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4856                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4857     i = 0;
4858     do
4859     {
4860         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4861                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4862             return 1;
4863     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4864
4865     return 0;
4866 }