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