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