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