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