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