wined3d: Handle stateblock capture for default lights created while recording.
[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                     fEndMenu = TRUE;
3123                     break;
3124
3125                 case VK_HOME:
3126                 case VK_END:
3127                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3128                                      NO_SELECTED_ITEM, FALSE, 0 );
3129                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3130                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3131                     break;
3132
3133                 case VK_UP:
3134                 case VK_DOWN: /* If on menu bar, pull-down the menu */
3135
3136                     menu = MENU_GetMenu( mt.hCurrentMenu );
3137                     if (!(menu->wFlags & MF_POPUP))
3138                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3139                     else      /* otherwise try to move selection */
3140                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
3141                                        (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3142                     break;
3143
3144                 case VK_LEFT:
3145                     MENU_KeyLeft( &mt, wFlags );
3146                     break;
3147
3148                 case VK_RIGHT:
3149                     MENU_KeyRight( &mt, wFlags );
3150                     break;
3151
3152                 case VK_ESCAPE:
3153                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
3154                     break;
3155
3156                 case VK_F1:
3157                     {
3158                         HELPINFO hi;
3159                         hi.cbSize = sizeof(HELPINFO);
3160                         hi.iContextType = HELPINFO_MENUITEM;
3161                         if (menu->FocusedItem == NO_SELECTED_ITEM)
3162                             hi.iCtrlId = 0;
3163                         else
3164                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3165                         hi.hItemHandle = hmenu;
3166                         hi.dwContextId = menu->dwContextHelpID;
3167                         hi.MousePos = msg.pt;
3168                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3169                         break;
3170                     }
3171
3172                 default:
3173                     break;
3174                 }
3175                 break;  /* WM_KEYDOWN */
3176
3177             case WM_CHAR:
3178             case WM_SYSCHAR:
3179                 {
3180                     UINT        pos;
3181
3182                     if (msg.wParam == '\r' || msg.wParam == ' ')
3183                     {
3184                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3185                         fEndMenu = (executedMenuId != -2);
3186
3187                         break;
3188                     }
3189
3190                       /* Hack to avoid control chars. */
3191                       /* We will find a better way real soon... */
3192                     if (msg.wParam < 32) break;
3193
3194                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3195                                               LOWORD(msg.wParam), FALSE );
3196                     if (pos == (UINT)-2) fEndMenu = TRUE;
3197                     else if (pos == (UINT)-1) MessageBeep(0);
3198                     else
3199                     {
3200                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3201                                 TRUE, 0 );
3202                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3203                         fEndMenu = (executedMenuId != -2);
3204                     }
3205                 }
3206                 break;
3207             }  /* switch(msg.message) - kbd */
3208         }
3209         else
3210         {
3211             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3212             DispatchMessageW( &msg );
3213             continue;
3214         }
3215
3216         if (!fEndMenu) fRemove = TRUE;
3217
3218         /* finally remove message from the queue */
3219
3220         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3221             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3222         else mt.trackFlags &= ~TF_SKIPREMOVE;
3223     }
3224     __FINALLY( release_capture )
3225
3226     /* If dropdown is still painted and the close box is clicked on
3227        then the menu will be destroyed as part of the DispatchMessage above.
3228        This will then invalidate the menu handle in mt.hTopMenu. We should
3229        check for this first.  */
3230     if( IsMenu( mt.hTopMenu ) )
3231     {
3232         menu = MENU_GetMenu( mt.hTopMenu );
3233
3234         if( IsWindow( mt.hOwnerWnd ) )
3235         {
3236             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3237
3238             if (menu && (menu->wFlags & MF_POPUP))
3239             {
3240                 DestroyWindow( menu->hWnd );
3241                 menu->hWnd = 0;
3242
3243                 if (!(wFlags & TPM_NONOTIFY))
3244                    SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3245                                  MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3246             }
3247             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3248             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3249         }
3250
3251         /* Reset the variable for hiding menu */
3252         if( menu ) menu->bTimeToHide = FALSE;
3253     }
3254
3255     /* The return value is only used by TrackPopupMenu */
3256     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3257     if (executedMenuId == -1) executedMenuId = 0;
3258     return executedMenuId;
3259 }
3260
3261 /***********************************************************************
3262  *           MENU_InitTracking
3263  */
3264 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3265 {
3266     POPUPMENU *menu;
3267     
3268     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3269
3270     HideCaret(0);
3271
3272     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3273     if (!(wFlags & TPM_NONOTIFY))
3274        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3275
3276     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3277
3278     if (!(wFlags & TPM_NONOTIFY))
3279     {
3280        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3281        /* If an app changed/recreated menu bar entries in WM_INITMENU
3282         * menu sizes will be recalculated once the menu created/shown.
3283         */
3284     }
3285     
3286     /* This makes the menus of applications built with Delphi work.
3287      * It also enables menus to be displayed in more than one window,
3288      * but there are some bugs left that need to be fixed in this case.
3289      */
3290     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3291     
3292     return TRUE;
3293 }
3294 /***********************************************************************
3295  *           MENU_ExitTracking
3296  */
3297 static BOOL MENU_ExitTracking(HWND hWnd)
3298 {
3299     TRACE("hwnd=%p\n", hWnd);
3300
3301     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3302     ShowCaret(0);
3303     top_popup = 0;
3304     top_popup_hmenu = NULL;
3305     return TRUE;
3306 }
3307
3308 /***********************************************************************
3309  *           MENU_TrackMouseMenuBar
3310  *
3311  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3312  */
3313 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3314 {
3315     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3316     UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3317
3318     TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3319
3320     if (IsMenu(hMenu))
3321     {
3322         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3323         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3324         MENU_ExitTracking(hWnd);
3325     }
3326 }
3327
3328
3329 /***********************************************************************
3330  *           MENU_TrackKbdMenuBar
3331  *
3332  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3333  */
3334 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3335 {
3336     UINT uItem = NO_SELECTED_ITEM;
3337     HMENU hTrackMenu;
3338     UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3339
3340     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3341
3342     /* find window that has a menu */
3343
3344     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3345         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3346
3347     /* check if we have to track a system menu */
3348
3349     hTrackMenu = GetMenu( hwnd );
3350     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3351     {
3352         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3353         hTrackMenu = get_win_sys_menu( hwnd );
3354         uItem = 0;
3355         wParam |= HTSYSMENU; /* prevent item lookup */
3356     }
3357
3358     if (!IsMenu( hTrackMenu )) return;
3359
3360     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3361
3362     if( wChar && wChar != ' ' )
3363     {
3364         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3365         if ( uItem >= (UINT)(-2) )
3366         {
3367             if( uItem == (UINT)(-1) ) MessageBeep(0);
3368             /* schedule end of menu tracking */
3369             wFlags |= TF_ENDMENU;
3370             goto track_menu;
3371         }
3372     }
3373
3374     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3375
3376     if (!(wParam & HTSYSMENU) || wChar == ' ')
3377     {
3378         if( uItem == NO_SELECTED_ITEM )
3379             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3380         else
3381             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3382     }
3383
3384 track_menu:
3385     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3386     MENU_ExitTracking( hwnd );
3387 }
3388
3389 /**********************************************************************
3390  *           TrackPopupMenuEx   (USER32.@)
3391  */
3392 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3393                               HWND hWnd, LPTPMPARAMS lpTpm )
3394 {
3395     BOOL ret = FALSE;
3396
3397     TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3398             hMenu, wFlags, x, y, hWnd, lpTpm,
3399             lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3400
3401     /* Parameter check */
3402     /* FIXME: this check is performed several times, here and in the called
3403        functions. That could be optimized */
3404     if (!MENU_GetMenu( hMenu ))
3405     {
3406         SetLastError( ERROR_INVALID_MENU_HANDLE );
3407         return FALSE;
3408     }
3409
3410     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3411
3412     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3413     if (!(wFlags & TPM_NONOTIFY))
3414         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3415
3416     if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3417         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3418                               lpTpm ? &lpTpm->rcExclude : NULL );
3419     MENU_ExitTracking(hWnd);
3420
3421     return ret;
3422 }
3423
3424 /**********************************************************************
3425  *           TrackPopupMenu   (USER32.@)
3426  *
3427  * Like the win32 API, the function return the command ID only if the
3428  * flag TPM_RETURNCMD is on.
3429  *
3430  */
3431 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3432                             INT nReserved, HWND hWnd, const RECT *lpRect )
3433 {
3434     return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3435 }
3436
3437 /***********************************************************************
3438  *           PopupMenuWndProc
3439  *
3440  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3441  */
3442 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3443 {
3444     TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3445
3446     switch(message)
3447     {
3448     case WM_CREATE:
3449         {
3450             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3451             SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3452             return 0;
3453         }
3454
3455     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3456         return MA_NOACTIVATE;
3457
3458     case WM_PAINT:
3459         {
3460             PAINTSTRUCT ps;
3461             BeginPaint( hwnd, &ps );
3462             MENU_DrawPopupMenu( hwnd, ps.hdc,
3463                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3464             EndPaint( hwnd, &ps );
3465             return 0;
3466         }
3467
3468     case WM_PRINTCLIENT:
3469         {
3470             MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3471                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3472             return 0;
3473         }
3474
3475     case WM_ERASEBKGND:
3476         return 1;
3477
3478     case WM_DESTROY:
3479         /* zero out global pointer in case resident popup window was destroyed. */
3480         if (hwnd == top_popup) {
3481             top_popup = 0;
3482             top_popup_hmenu = NULL;
3483         }
3484         break;
3485
3486     case WM_SHOWWINDOW:
3487
3488         if( wParam )
3489         {
3490             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3491         }
3492         else
3493             SetWindowLongPtrW( hwnd, 0, 0 );
3494         break;
3495
3496     case MM_SETMENUHANDLE:
3497         SetWindowLongPtrW( hwnd, 0, wParam );
3498         break;
3499
3500     case MM_GETMENUHANDLE:
3501     case MN_GETHMENU:
3502         return GetWindowLongPtrW( hwnd, 0 );
3503
3504     default:
3505         return DefWindowProcW( hwnd, message, wParam, lParam );
3506     }
3507     return 0;
3508 }
3509
3510
3511 /***********************************************************************
3512  *           MENU_GetMenuBarHeight
3513  *
3514  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3515  */
3516 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3517                               INT orgX, INT orgY )
3518 {
3519     HDC hdc;
3520     RECT rectBar;
3521     LPPOPUPMENU lppop;
3522
3523     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3524
3525     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3526
3527     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3528     SelectObject( hdc, get_menu_font(FALSE));
3529     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3530     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3531     ReleaseDC( hwnd, hdc );
3532     return lppop->Height;
3533 }
3534
3535
3536 /*******************************************************************
3537  *         ChangeMenuA    (USER32.@)
3538  */
3539 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3540                              UINT id, UINT flags )
3541 {
3542     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3543     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3544                                                  id, data );
3545     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3546     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3547                                                 id, data );
3548     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3549                                               flags & MF_BYPOSITION ? pos : id,
3550                                               flags & ~MF_REMOVE );
3551     /* Default: MF_INSERT */
3552     return InsertMenuA( hMenu, pos, flags, id, data );
3553 }
3554
3555
3556 /*******************************************************************
3557  *         ChangeMenuW    (USER32.@)
3558  */
3559 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3560                              UINT id, UINT flags )
3561 {
3562     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3563     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3564                                                  id, data );
3565     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3566     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3567                                                 id, data );
3568     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3569                                               flags & MF_BYPOSITION ? pos : id,
3570                                               flags & ~MF_REMOVE );
3571     /* Default: MF_INSERT */
3572     return InsertMenuW( hMenu, pos, flags, id, data );
3573 }
3574
3575
3576 /*******************************************************************
3577  *         CheckMenuItem    (USER32.@)
3578  */
3579 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3580 {
3581     MENUITEM *item;
3582     DWORD ret;
3583
3584     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3585     ret = item->fState & MF_CHECKED;
3586     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3587     else item->fState &= ~MF_CHECKED;
3588     return ret;
3589 }
3590
3591
3592 /**********************************************************************
3593  *         EnableMenuItem    (USER32.@)
3594  */
3595 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3596 {
3597     UINT    oldflags;
3598     MENUITEM *item;
3599     POPUPMENU *menu;
3600
3601     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3602
3603     /* Get the Popupmenu to access the owner menu */
3604     if (!(menu = MENU_GetMenu(hMenu)))
3605         return (UINT)-1;
3606
3607     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3608         return (UINT)-1;
3609
3610     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3611     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3612
3613     /* If the close item in the system menu change update the close button */
3614     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3615     {
3616         if (menu->hSysMenuOwner != 0)
3617         {
3618             RECT rc;
3619             POPUPMENU* parentMenu;
3620
3621             /* Get the parent menu to access*/
3622             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3623                 return (UINT)-1;
3624
3625             /* Refresh the frame to reflect the change */
3626             GetWindowRect(parentMenu->hWnd, &rc);
3627             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3628             rc.bottom = 0;
3629             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3630         }
3631     }
3632
3633     return oldflags;
3634 }
3635
3636
3637 /*******************************************************************
3638  *         GetMenuStringA    (USER32.@)
3639  */
3640 INT WINAPI GetMenuStringA(
3641         HMENU hMenu,    /* [in] menuhandle */
3642         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3643         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3644         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3645         UINT wFlags     /* [in] MF_ flags */
3646 ) {
3647     MENUITEM *item;
3648
3649     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3650     if (str && nMaxSiz) str[0] = '\0';
3651     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3652         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3653         return 0;
3654     }
3655     if (!item->text) return 0;
3656     if (!str || !nMaxSiz) return strlenW(item->text);
3657     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3658         str[nMaxSiz-1] = 0;
3659     TRACE("returning %s\n", debugstr_a(str));
3660     return strlen(str);
3661 }
3662
3663
3664 /*******************************************************************
3665  *         GetMenuStringW    (USER32.@)
3666  */
3667 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3668                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3669 {
3670     MENUITEM *item;
3671
3672     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3673     if (str && nMaxSiz) str[0] = '\0';
3674     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3675         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3676         return 0;
3677     }
3678     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3679     if( !(item->text)) {
3680         str[0] = 0;
3681         return 0;
3682     }
3683     lstrcpynW( str, item->text, nMaxSiz );
3684     TRACE("returning %s\n", debugstr_w(str));
3685     return strlenW(str);
3686 }
3687
3688
3689 /**********************************************************************
3690  *         HiliteMenuItem    (USER32.@)
3691  */
3692 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3693                                 UINT wHilite )
3694 {
3695     LPPOPUPMENU menu;
3696     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3697     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3698     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3699     if (menu->FocusedItem == wItemID) return TRUE;
3700     MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3701     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3702     return TRUE;
3703 }
3704
3705
3706 /**********************************************************************
3707  *         GetMenuState    (USER32.@)
3708  */
3709 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3710 {
3711     MENUITEM *item;
3712     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3713     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3714     debug_print_menuitem ("  item: ", item, "");
3715     if (item->fType & MF_POPUP)
3716     {
3717         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3718         if (!menu) return -1;
3719         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3720     }
3721     else
3722     {
3723         /* We used to (from way back then) mask the result to 0xff.  */
3724         /* I don't know why and it seems wrong as the documented */
3725         /* return flag MF_SEPARATOR is outside that mask.  */
3726         return (item->fType | item->fState);
3727     }
3728 }
3729
3730
3731 /**********************************************************************
3732  *         GetMenuItemCount    (USER32.@)
3733  */
3734 INT WINAPI GetMenuItemCount( HMENU hMenu )
3735 {
3736     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3737     if (!menu) return -1;
3738     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3739     return menu->nItems;
3740 }
3741
3742
3743 /**********************************************************************
3744  *         GetMenuItemID    (USER32.@)
3745  */
3746 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3747 {
3748     MENUITEM * lpmi;
3749
3750     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3751     if (lpmi->fType & MF_POPUP) return -1;
3752     return lpmi->wID;
3753
3754 }
3755
3756
3757 /**********************************************************************
3758  *         MENU_mnu2mnuii
3759  *
3760  * Uses flags, id and text ptr, passed by InsertMenu() and
3761  * ModifyMenu() to setup a MenuItemInfo structure.
3762  */
3763 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3764         LPMENUITEMINFOW pmii)
3765 {
3766     ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3767     pmii->cbSize = sizeof( MENUITEMINFOW);
3768     pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3769     /* setting bitmap clears text and vice versa */
3770     if( IS_STRING_ITEM(flags)) {
3771         pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3772         if( !str)
3773             flags |= MF_SEPARATOR;
3774         /* Item beginning with a backspace is a help item */
3775         /* FIXME: wrong place, this is only true in win16 */
3776         else if( *str == '\b') {
3777             flags |= MF_HELP;
3778             str++;
3779         }
3780         pmii->dwTypeData = (LPWSTR)str;
3781     } else if( flags & MFT_BITMAP){
3782         pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3783         pmii->hbmpItem = ULongToHandle(LOWORD(str));
3784     }
3785     if( flags & MF_OWNERDRAW){
3786         pmii->fMask |= MIIM_DATA;
3787         pmii->dwItemData = (ULONG_PTR) str;
3788     }
3789     if( flags & MF_POPUP) {
3790         pmii->fMask |= MIIM_SUBMENU;
3791         pmii->hSubMenu = (HMENU)id;
3792     }
3793     if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3794     pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3795     pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3796     pmii->wID = (UINT)id;
3797 }
3798
3799
3800 /*******************************************************************
3801  *         InsertMenuW    (USER32.@)
3802  */
3803 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3804                          UINT_PTR id, LPCWSTR str )
3805 {
3806     MENUITEM *item;
3807     MENUITEMINFOW mii;
3808
3809     if (IS_STRING_ITEM(flags) && str)
3810         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3811               hMenu, pos, flags, id, debugstr_w(str) );
3812     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3813                hMenu, pos, flags, id, str );
3814
3815     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3816     MENU_mnu2mnuii( flags, id, str, &mii);
3817     if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3818     {
3819         RemoveMenu( hMenu, pos, flags );
3820         return FALSE;
3821     }
3822
3823     item->hCheckBit = item->hUnCheckBit = 0;
3824     return TRUE;
3825 }
3826
3827
3828 /*******************************************************************
3829  *         InsertMenuA    (USER32.@)
3830  */
3831 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3832                          UINT_PTR id, LPCSTR str )
3833 {
3834     BOOL ret = FALSE;
3835
3836     if (IS_STRING_ITEM(flags) && str)
3837     {
3838         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3839         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3840         if (newstr)
3841         {
3842             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3843             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3844             HeapFree( GetProcessHeap(), 0, newstr );
3845         }
3846         return ret;
3847     }
3848     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3849 }
3850
3851
3852 /*******************************************************************
3853  *         AppendMenuA    (USER32.@)
3854  */
3855 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3856                          UINT_PTR id, LPCSTR data )
3857 {
3858     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3859 }
3860
3861
3862 /*******************************************************************
3863  *         AppendMenuW    (USER32.@)
3864  */
3865 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3866                          UINT_PTR id, LPCWSTR data )
3867 {
3868     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3869 }
3870
3871
3872 /**********************************************************************
3873  *         RemoveMenu    (USER32.@)
3874  */
3875 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3876 {
3877     LPPOPUPMENU menu;
3878     MENUITEM *item;
3879
3880     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3881     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3882     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3883
3884       /* Remove item */
3885
3886     MENU_FreeItemData( item );
3887
3888     if (--menu->nItems == 0)
3889     {
3890         HeapFree( GetProcessHeap(), 0, menu->items );
3891         menu->items = NULL;
3892     }
3893     else
3894     {
3895         while(nPos < menu->nItems)
3896         {
3897             *item = *(item+1);
3898             item++;
3899             nPos++;
3900         }
3901         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3902                                    menu->nItems * sizeof(MENUITEM) );
3903     }
3904     return TRUE;
3905 }
3906
3907
3908 /**********************************************************************
3909  *         DeleteMenu    (USER32.@)
3910  */
3911 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3912 {
3913     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3914     if (!item) return FALSE;
3915     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3916       /* nPos is now the position of the item */
3917     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3918     return TRUE;
3919 }
3920
3921
3922 /*******************************************************************
3923  *         ModifyMenuW    (USER32.@)
3924  */
3925 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3926                          UINT_PTR id, LPCWSTR str )
3927 {
3928     MENUITEM *item;
3929     MENUITEMINFOW mii;
3930
3931     if (IS_STRING_ITEM(flags))
3932         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3933     else
3934         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3935
3936     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3937     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3938     MENU_mnu2mnuii( flags, id, str, &mii);
3939     return SetMenuItemInfo_common( item, &mii, TRUE);
3940 }
3941
3942
3943 /*******************************************************************
3944  *         ModifyMenuA    (USER32.@)
3945  */
3946 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3947                          UINT_PTR id, LPCSTR str )
3948 {
3949     BOOL ret = FALSE;
3950
3951     if (IS_STRING_ITEM(flags) && str)
3952     {
3953         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3954         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3955         if (newstr)
3956         {
3957             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3958             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3959             HeapFree( GetProcessHeap(), 0, newstr );
3960         }
3961         return ret;
3962     }
3963     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3964 }
3965
3966
3967 /**********************************************************************
3968  *         CreatePopupMenu    (USER32.@)
3969  */
3970 HMENU WINAPI CreatePopupMenu(void)
3971 {
3972     HMENU hmenu;
3973     POPUPMENU *menu;
3974
3975     if (!(hmenu = CreateMenu())) return 0;
3976     menu = MENU_GetMenu( hmenu );
3977     menu->wFlags |= MF_POPUP;
3978     menu->bTimeToHide = FALSE;
3979     return hmenu;
3980 }
3981
3982
3983 /**********************************************************************
3984  *         GetMenuCheckMarkDimensions    (USER.417)
3985  *         GetMenuCheckMarkDimensions    (USER32.@)
3986  */
3987 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3988 {
3989     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3990 }
3991
3992
3993 /**********************************************************************
3994  *         SetMenuItemBitmaps    (USER32.@)
3995  */
3996 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3997                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3998 {
3999     MENUITEM *item;
4000
4001     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4002
4003     if (!hNewCheck && !hNewUnCheck)
4004     {
4005         item->fState &= ~MF_USECHECKBITMAPS;
4006     }
4007     else  /* Install new bitmaps */
4008     {
4009         item->hCheckBit = hNewCheck;
4010         item->hUnCheckBit = hNewUnCheck;
4011         item->fState |= MF_USECHECKBITMAPS;
4012     }
4013     return TRUE;
4014 }
4015
4016
4017 /**********************************************************************
4018  *         CreateMenu    (USER32.@)
4019  */
4020 HMENU WINAPI CreateMenu(void)
4021 {
4022     HMENU hMenu;
4023     LPPOPUPMENU menu;
4024
4025     if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4026     menu->FocusedItem = NO_SELECTED_ITEM;
4027     menu->bTimeToHide = FALSE;
4028
4029     if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4030
4031     TRACE("return %p\n", hMenu );
4032
4033     return hMenu;
4034 }
4035
4036
4037 /**********************************************************************
4038  *         DestroyMenu    (USER32.@)
4039  */
4040 BOOL WINAPI DestroyMenu( HMENU hMenu )
4041 {
4042     LPPOPUPMENU lppop;
4043
4044     TRACE("(%p)\n", hMenu);
4045
4046     if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4047     if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4048
4049     /* DestroyMenu should not destroy system menu popup owner */
4050     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4051     {
4052         DestroyWindow( lppop->hWnd );
4053         lppop->hWnd = 0;
4054     }
4055
4056     if (lppop->items) /* recursively destroy submenus */
4057     {
4058         int i;
4059         MENUITEM *item = lppop->items;
4060         for (i = lppop->nItems; i > 0; i--, item++)
4061         {
4062             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4063             MENU_FreeItemData( item );
4064         }
4065         HeapFree( GetProcessHeap(), 0, lppop->items );
4066     }
4067     HeapFree( GetProcessHeap(), 0, lppop );
4068     return TRUE;
4069 }
4070
4071
4072 /**********************************************************************
4073  *         GetSystemMenu    (USER32.@)
4074  */
4075 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4076 {
4077     WND *wndPtr = WIN_GetPtr( hWnd );
4078     HMENU retvalue = 0;
4079
4080     if (wndPtr == WND_DESKTOP) return 0;
4081     if (wndPtr == WND_OTHER_PROCESS)
4082     {
4083         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4084     }
4085     else if (wndPtr)
4086     {
4087         if (wndPtr->hSysMenu && bRevert)
4088         {
4089             DestroyMenu(wndPtr->hSysMenu);
4090             wndPtr->hSysMenu = 0;
4091         }
4092
4093         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4094             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4095
4096         if( wndPtr->hSysMenu )
4097         {
4098             POPUPMENU *menu;
4099             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4100
4101             /* Store the dummy sysmenu handle to facilitate the refresh */
4102             /* of the close button if the SC_CLOSE item change */
4103             menu = MENU_GetMenu(retvalue);
4104             if ( menu )
4105                menu->hSysMenuOwner = wndPtr->hSysMenu;
4106         }
4107         WIN_ReleasePtr( wndPtr );
4108     }
4109     return bRevert ? 0 : retvalue;
4110 }
4111
4112
4113 /*******************************************************************
4114  *         SetSystemMenu    (USER32.@)
4115  */
4116 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4117 {
4118     WND *wndPtr = WIN_GetPtr( hwnd );
4119
4120     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4121     {
4122         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4123         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4124         WIN_ReleasePtr( wndPtr );
4125         return TRUE;
4126     }
4127     return FALSE;
4128 }
4129
4130
4131 /**********************************************************************
4132  *         GetMenu    (USER32.@)
4133  */
4134 HMENU WINAPI GetMenu( HWND hWnd )
4135 {
4136     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4137     TRACE("for %p returning %p\n", hWnd, retvalue);
4138     return retvalue;
4139 }
4140
4141 /**********************************************************************
4142  *         GetMenuBarInfo    (USER32.@)
4143  */
4144 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4145 {
4146     FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4147     return FALSE;
4148 }
4149
4150 /**********************************************************************
4151  *         MENU_SetMenu
4152  *
4153  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4154  * SetWindowPos call that would result if SetMenu were called directly.
4155  */
4156 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4157 {
4158     TRACE("(%p, %p);\n", hWnd, hMenu);
4159
4160     if (hMenu && !IsMenu(hMenu))
4161     {
4162         WARN("hMenu %p is not a menu handle\n", hMenu);
4163         return FALSE;
4164     }
4165     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4166         return FALSE;
4167
4168     hWnd = WIN_GetFullHandle( hWnd );
4169     if (GetCapture() == hWnd)
4170         set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
4171
4172     if (hMenu != 0)
4173     {
4174         LPPOPUPMENU lpmenu;
4175
4176         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4177
4178         lpmenu->hWnd = hWnd;
4179         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4180     }
4181     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4182     return TRUE;
4183 }
4184
4185
4186 /**********************************************************************
4187  *         SetMenu    (USER32.@)
4188  */
4189 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4190 {   
4191     if(!MENU_SetMenu(hWnd, hMenu))
4192         return FALSE;
4193  
4194     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4195                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4196     return TRUE;
4197 }
4198
4199
4200 /**********************************************************************
4201  *         GetSubMenu    (USER32.@)
4202  */
4203 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4204 {
4205     MENUITEM * lpmi;
4206
4207     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4208     if (!(lpmi->fType & MF_POPUP)) return 0;
4209     return lpmi->hSubMenu;
4210 }
4211
4212
4213 /**********************************************************************
4214  *         DrawMenuBar    (USER32.@)
4215  */
4216 BOOL WINAPI DrawMenuBar( HWND hWnd )
4217 {
4218     LPPOPUPMENU lppop;
4219     HMENU hMenu = GetMenu(hWnd);
4220
4221     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4222         return FALSE;
4223     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4224
4225     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4226     lppop->hwndOwner = hWnd;
4227     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4228                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4229     return TRUE;
4230 }
4231
4232 /***********************************************************************
4233  *           DrawMenuBarTemp   (USER32.@)
4234  *
4235  * UNDOCUMENTED !!
4236  *
4237  * called by W98SE desk.cpl Control Panel Applet
4238  *
4239  * Not 100% sure about the param names, but close.
4240  */
4241 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4242 {
4243     LPPOPUPMENU lppop;
4244     UINT i,retvalue;
4245     HFONT hfontOld = 0;
4246     BOOL flat_menu = FALSE;
4247
4248     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4249
4250     if (!hMenu)
4251         hMenu = GetMenu(hwnd);
4252
4253     if (!hFont)
4254         hFont = get_menu_font(FALSE);
4255
4256     lppop = MENU_GetMenu( hMenu );
4257     if (lppop == NULL || lprect == NULL)
4258     {
4259         retvalue = GetSystemMetrics(SM_CYMENU);
4260         goto END;
4261     }
4262
4263     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4264
4265     hfontOld = SelectObject( hDC, hFont);
4266
4267     if (lppop->Height == 0)
4268         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4269
4270     lprect->bottom = lprect->top + lppop->Height;
4271
4272     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4273
4274     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4275     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4276     LineTo( hDC, lprect->right, lprect->bottom );
4277
4278     if (lppop->nItems == 0)
4279     {
4280         retvalue = GetSystemMetrics(SM_CYMENU);
4281         goto END;
4282     }
4283
4284     for (i = 0; i < lppop->nItems; i++)
4285     {
4286         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4287                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4288     }
4289     retvalue = lppop->Height;
4290
4291 END:
4292     if (hfontOld) SelectObject (hDC, hfontOld);
4293     return retvalue;
4294 }
4295
4296 /***********************************************************************
4297  *           EndMenu   (USER.187)
4298  *           EndMenu   (USER32.@)
4299  */
4300 BOOL WINAPI EndMenu(void)
4301 {
4302     /* if we are in the menu code, and it is active */
4303     if (!fEndMenu && top_popup)
4304     {
4305         /* terminate the menu handling code */
4306         fEndMenu = TRUE;
4307
4308         /* needs to be posted to wakeup the internal menu handler */
4309         /* which will now terminate the menu, in the event that */
4310         /* the main window was minimized, or lost focus, so we */
4311         /* don't end up with an orphaned menu */
4312         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4313     }
4314     return fEndMenu;
4315 }
4316
4317
4318 /*****************************************************************
4319  *        LoadMenuA   (USER32.@)
4320  */
4321 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4322 {
4323     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4324     if (!hrsrc) return 0;
4325     return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4326 }
4327
4328
4329 /*****************************************************************
4330  *        LoadMenuW   (USER32.@)
4331  */
4332 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4333 {
4334     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4335     if (!hrsrc) return 0;
4336     return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4337 }
4338
4339
4340 /**********************************************************************
4341  *          LoadMenuIndirectW    (USER32.@)
4342  */
4343 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4344 {
4345     HMENU hMenu;
4346     WORD version, offset;
4347     LPCSTR p = template;
4348
4349     version = GET_WORD(p);
4350     p += sizeof(WORD);
4351     TRACE("%p, ver %d\n", template, version );
4352     switch (version)
4353     {
4354       case 0: /* standard format is version of 0 */
4355         offset = GET_WORD(p);
4356         p += sizeof(WORD) + offset;
4357         if (!(hMenu = CreateMenu())) return 0;
4358         if (!MENU_ParseResource( p, hMenu ))
4359           {
4360             DestroyMenu( hMenu );
4361             return 0;
4362           }
4363         return hMenu;
4364       case 1: /* extended format is version of 1 */
4365         offset = GET_WORD(p);
4366         p += sizeof(WORD) + offset;
4367         if (!(hMenu = CreateMenu())) return 0;
4368         if (!MENUEX_ParseResource( p, hMenu))
4369           {
4370             DestroyMenu( hMenu );
4371             return 0;
4372           }
4373         return hMenu;
4374       default:
4375         ERR("version %d not supported.\n", version);
4376         return 0;
4377     }
4378 }
4379
4380
4381 /**********************************************************************
4382  *          LoadMenuIndirectA    (USER32.@)
4383  */
4384 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4385 {
4386     return LoadMenuIndirectW( template );
4387 }
4388
4389
4390 /**********************************************************************
4391  *              IsMenu    (USER32.@)
4392  */
4393 BOOL WINAPI IsMenu(HMENU hmenu)
4394 {
4395     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4396
4397     if (!menu)
4398     {
4399         SetLastError(ERROR_INVALID_MENU_HANDLE);
4400         return FALSE;
4401     }
4402     return TRUE;
4403 }
4404
4405 /**********************************************************************
4406  *              GetMenuItemInfo_common
4407  */
4408
4409 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4410                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4411 {
4412     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4413
4414     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4415
4416     if (!menu) {
4417         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4418         return FALSE;
4419     }
4420     
4421     if( lpmii->fMask & MIIM_TYPE) {
4422         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4423             WARN("invalid combination of fMask bits used\n");
4424             /* this does not happen on Win9x/ME */
4425             SetLastError( ERROR_INVALID_PARAMETER);
4426             return FALSE;
4427         }
4428         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4429         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4430         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4431         if( lpmii->fType & MFT_BITMAP) {
4432             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4433             lpmii->cch = 0;
4434         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4435             /* this does not happen on Win9x/ME */
4436             lpmii->dwTypeData = 0;
4437             lpmii->cch = 0;
4438         }
4439     }
4440
4441     /* copy the text string */
4442     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4443          if( !menu->text ) {
4444                 if(lpmii->dwTypeData && lpmii->cch) {
4445                     lpmii->cch = 0;
4446                     if( unicode)
4447                         *((WCHAR *)lpmii->dwTypeData) = 0;
4448                     else
4449                         *((CHAR *)lpmii->dwTypeData) = 0;
4450                 }
4451          } else {
4452             int len;
4453             if (unicode)
4454             {
4455                 len = strlenW(menu->text);
4456                 if(lpmii->dwTypeData && lpmii->cch)
4457                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4458             }
4459             else
4460             {
4461                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4462                         0, NULL, NULL ) - 1;
4463                 if(lpmii->dwTypeData && lpmii->cch)
4464                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4465                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4466                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4467             }
4468             /* if we've copied a substring we return its length */
4469             if(lpmii->dwTypeData && lpmii->cch)
4470                 if (lpmii->cch <= len + 1)
4471                     lpmii->cch--;
4472                 else
4473                     lpmii->cch = len;
4474             else {
4475                 /* return length of string */
4476                 /* not on Win9x/ME if fType & MFT_BITMAP */
4477                 lpmii->cch = len;
4478             }
4479         }
4480     }
4481
4482     if (lpmii->fMask & MIIM_FTYPE)
4483         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4484
4485     if (lpmii->fMask & MIIM_BITMAP)
4486         lpmii->hbmpItem = menu->hbmpItem;
4487
4488     if (lpmii->fMask & MIIM_STATE)
4489         lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4490
4491     if (lpmii->fMask & MIIM_ID)
4492         lpmii->wID = menu->wID;
4493
4494     if (lpmii->fMask & MIIM_SUBMENU)
4495         lpmii->hSubMenu = menu->hSubMenu;
4496     else {
4497         /* hSubMenu is always cleared 
4498          * (not on Win9x/ME ) */
4499         lpmii->hSubMenu = 0;
4500     }
4501
4502     if (lpmii->fMask & MIIM_CHECKMARKS) {
4503         lpmii->hbmpChecked = menu->hCheckBit;
4504         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4505     }
4506     if (lpmii->fMask & MIIM_DATA)
4507         lpmii->dwItemData = menu->dwItemData;
4508
4509   return TRUE;
4510 }
4511
4512 /**********************************************************************
4513  *              GetMenuItemInfoA    (USER32.@)
4514  */
4515 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4516                                   LPMENUITEMINFOA lpmii)
4517 {
4518     BOOL ret;
4519     MENUITEMINFOA mii;
4520     if( lpmii->cbSize != sizeof( mii) &&
4521             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4522         SetLastError( ERROR_INVALID_PARAMETER);
4523         return FALSE;
4524     }
4525     memcpy( &mii, lpmii, lpmii->cbSize);
4526     mii.cbSize = sizeof( mii);
4527     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4528                                     (LPMENUITEMINFOW)&mii, FALSE);
4529     mii.cbSize = lpmii->cbSize;
4530     memcpy( lpmii, &mii, mii.cbSize);
4531     return ret;
4532 }
4533
4534 /**********************************************************************
4535  *              GetMenuItemInfoW    (USER32.@)
4536  */
4537 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4538                                   LPMENUITEMINFOW lpmii)
4539 {
4540     BOOL ret;
4541     MENUITEMINFOW mii;
4542     if( lpmii->cbSize != sizeof( mii) &&
4543             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4544         SetLastError( ERROR_INVALID_PARAMETER);
4545         return FALSE;
4546     }
4547     memcpy( &mii, lpmii, lpmii->cbSize);
4548     mii.cbSize = sizeof( mii);
4549     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4550     mii.cbSize = lpmii->cbSize;
4551     memcpy( lpmii, &mii, mii.cbSize);
4552     return ret;
4553 }
4554
4555
4556 /* set a menu item text from a ASCII or Unicode string */
4557 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4558 {
4559     if (!text)
4560         menu->text = NULL;
4561     else if (unicode)
4562     {
4563         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4564             strcpyW( menu->text, text );
4565     }
4566     else
4567     {
4568         LPCSTR str = (LPCSTR)text;
4569         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4570         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4571             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4572     }
4573 }
4574
4575
4576 /**********************************************************************
4577  *              MENU_depth
4578  *
4579  * detect if there are loops in the menu tree (or the depth is too large)
4580  */
4581 static int MENU_depth( POPUPMENU *pmenu, int depth)
4582 {
4583     int i;
4584     MENUITEM *item;
4585     int subdepth;
4586
4587     depth++;
4588     if( depth > MAXMENUDEPTH) return depth;
4589     item = pmenu->items;
4590     subdepth = depth;
4591     for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4592         POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4593         if( psubmenu){
4594             int bdepth = MENU_depth( psubmenu, depth);
4595             if( bdepth > subdepth) subdepth = bdepth;
4596         }
4597         if( subdepth > MAXMENUDEPTH)
4598             TRACE("<- hmenu %p\n", item->hSubMenu);
4599     }
4600     return subdepth;
4601 }
4602
4603
4604 /**********************************************************************
4605  *              SetMenuItemInfo_common
4606  *
4607  * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4608  * MIIM_BITMAP and MIIM_STRING flags instead.
4609  */
4610
4611 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4612                                        const MENUITEMINFOW *lpmii,
4613                                        BOOL unicode)
4614 {
4615     if (!menu) return FALSE;
4616
4617     debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4618
4619     if (lpmii->fMask & MIIM_FTYPE ) {
4620         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4621         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4622     }
4623     if (lpmii->fMask & MIIM_STRING ) {
4624         /* free the string when used */
4625         HeapFree(GetProcessHeap(), 0, menu->text);
4626         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4627     }
4628
4629     if (lpmii->fMask & MIIM_STATE)
4630          /* Other menu items having MFS_DEFAULT are not converted
4631            to normal items */
4632          menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4633
4634     if (lpmii->fMask & MIIM_ID)
4635         menu->wID = lpmii->wID;
4636
4637     if (lpmii->fMask & MIIM_SUBMENU) {
4638         menu->hSubMenu = lpmii->hSubMenu;
4639         if (menu->hSubMenu) {
4640             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4641             if (subMenu) {
4642                 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4643                     ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4644                     menu->hSubMenu = 0;
4645                     return FALSE;
4646                 }
4647                 subMenu->wFlags |= MF_POPUP;
4648                 menu->fType |= MF_POPUP;
4649             } else {
4650                 SetLastError( ERROR_INVALID_PARAMETER);
4651                 return FALSE;
4652             }
4653         }
4654         else
4655             menu->fType &= ~MF_POPUP;
4656     }
4657
4658     if (lpmii->fMask & MIIM_CHECKMARKS)
4659     {
4660         menu->hCheckBit = lpmii->hbmpChecked;
4661         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4662     }
4663     if (lpmii->fMask & MIIM_DATA)
4664         menu->dwItemData = lpmii->dwItemData;
4665
4666     if (lpmii->fMask & MIIM_BITMAP)
4667         menu->hbmpItem = lpmii->hbmpItem;
4668
4669     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4670         menu->fType |= MFT_SEPARATOR;
4671
4672     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4673     return TRUE;
4674 }
4675
4676 /**********************************************************************
4677  *              MENU_NormalizeMenuItemInfoStruct
4678  *
4679  * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4680  * check, copy and extend the MENUITEMINFO struct from the version that the application
4681  * supplied to the version used by wine source. */
4682 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4683                                               MENUITEMINFOW *pmii_out )
4684 {
4685     /* do we recognize the size? */
4686     if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4687             pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4688         SetLastError( ERROR_INVALID_PARAMETER);
4689         return FALSE;
4690     }
4691     /* copy the fields that we have */
4692     memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4693     /* if the hbmpItem member is missing then extend */
4694     if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4695         pmii_out->cbSize = sizeof( MENUITEMINFOW);
4696         pmii_out->hbmpItem = NULL;
4697     }
4698     /* test for invalid bit combinations */
4699     if( (pmii_out->fMask & MIIM_TYPE &&
4700          pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4701         (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4702         WARN("invalid combination of fMask bits used\n");
4703         /* this does not happen on Win9x/ME */
4704         SetLastError( ERROR_INVALID_PARAMETER);
4705         return FALSE;
4706     }
4707     /* convert old style (MIIM_TYPE) to the new */
4708     if( pmii_out->fMask & MIIM_TYPE){
4709         pmii_out->fMask |= MIIM_FTYPE;
4710         if( IS_STRING_ITEM(pmii_out->fType)){
4711             pmii_out->fMask |= MIIM_STRING;
4712         } else if( (pmii_out->fType) & MFT_BITMAP){
4713             pmii_out->fMask |= MIIM_BITMAP;
4714             pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4715         }
4716     }
4717     return TRUE;
4718 }
4719
4720 /**********************************************************************
4721  *              SetMenuItemInfoA    (USER32.@)
4722  */
4723 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4724                                  const MENUITEMINFOA *lpmii)
4725 {
4726     MENUITEMINFOW mii;
4727
4728     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4729
4730     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4731
4732     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4733                                   &mii, FALSE);
4734 }
4735
4736 /**********************************************************************
4737  *              SetMenuItemInfoW    (USER32.@)
4738  */
4739 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4740                                  const MENUITEMINFOW *lpmii)
4741 {
4742     MENUITEMINFOW mii;
4743
4744     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4745
4746     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4747     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4748                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4749 }
4750
4751 /**********************************************************************
4752  *              SetMenuDefaultItem    (USER32.@)
4753  *
4754  */
4755 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4756 {
4757         UINT i;
4758         POPUPMENU *menu;
4759         MENUITEM *item;
4760
4761         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4762
4763         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4764
4765         /* reset all default-item flags */
4766         item = menu->items;
4767         for (i = 0; i < menu->nItems; i++, item++)
4768         {
4769             item->fState &= ~MFS_DEFAULT;
4770         }
4771
4772         /* no default item */
4773         if ( -1 == uItem)
4774         {
4775             return TRUE;
4776         }
4777
4778         item = menu->items;
4779         if ( bypos )
4780         {
4781             if ( uItem >= menu->nItems ) return FALSE;
4782             item[uItem].fState |= MFS_DEFAULT;
4783             return TRUE;
4784         }
4785         else
4786         {
4787             for (i = 0; i < menu->nItems; i++, item++)
4788             {
4789                 if (item->wID == uItem)
4790                 {
4791                      item->fState |= MFS_DEFAULT;
4792                      return TRUE;
4793                 }
4794             }
4795
4796         }
4797         return FALSE;
4798 }
4799
4800 /**********************************************************************
4801  *              GetMenuDefaultItem    (USER32.@)
4802  */
4803 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4804 {
4805         POPUPMENU *menu;
4806         MENUITEM * item;
4807         UINT i = 0;
4808
4809         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4810
4811         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4812
4813         /* find default item */
4814         item = menu->items;
4815
4816         /* empty menu */
4817         if (! item) return -1;
4818
4819         while ( !( item->fState & MFS_DEFAULT ) )
4820         {
4821             i++; item++;
4822             if  (i >= menu->nItems ) return -1;
4823         }
4824
4825         /* default: don't return disabled items */
4826         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4827
4828         /* search rekursiv when needed */
4829         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4830         {
4831             UINT ret;
4832             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4833             if ( -1 != ret ) return ret;
4834
4835             /* when item not found in submenu, return the popup item */
4836         }
4837         return ( bypos ) ? i : item->wID;
4838
4839 }
4840
4841
4842 /**********************************************************************
4843  *              InsertMenuItemA    (USER32.@)
4844  */
4845 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4846                                 const MENUITEMINFOA *lpmii)
4847 {
4848     MENUITEM *item;
4849     MENUITEMINFOW mii;
4850
4851     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4852
4853     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4854
4855     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4856     return SetMenuItemInfo_common(item, &mii, FALSE);
4857 }
4858
4859
4860 /**********************************************************************
4861  *              InsertMenuItemW    (USER32.@)
4862  */
4863 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4864                                 const MENUITEMINFOW *lpmii)
4865 {
4866     MENUITEM *item;
4867     MENUITEMINFOW mii;
4868
4869     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4870
4871     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4872
4873     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4874     return SetMenuItemInfo_common(item, &mii, TRUE);
4875 }
4876
4877 /**********************************************************************
4878  *              CheckMenuRadioItem    (USER32.@)
4879  */
4880
4881 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4882                                    UINT first, UINT last, UINT check,
4883                                    UINT bypos)
4884 {
4885     BOOL done = FALSE;
4886     UINT i;
4887     MENUITEM *mi_first = NULL, *mi_check;
4888     HMENU m_first, m_check;
4889
4890     for (i = first; i <= last; i++)
4891     {
4892         UINT pos = i;
4893
4894         if (!mi_first)
4895         {
4896             m_first = hMenu;
4897             mi_first = MENU_FindItem(&m_first, &pos, bypos);
4898             if (!mi_first) continue;
4899             mi_check = mi_first;
4900             m_check = m_first;
4901         }
4902         else
4903         {
4904             m_check = hMenu;
4905             mi_check = MENU_FindItem(&m_check, &pos, bypos);
4906             if (!mi_check) continue;
4907         }
4908
4909         if (m_first != m_check) continue;
4910         if (mi_check->fType == MFT_SEPARATOR) continue;
4911
4912         if (i == check)
4913         {
4914             mi_check->fType |= MFT_RADIOCHECK;
4915             mi_check->fState |= MFS_CHECKED;
4916             done = TRUE;
4917         }
4918         else
4919         {
4920             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4921             mi_check->fState &= ~MFS_CHECKED;
4922         }
4923     }
4924
4925     return done;
4926 }
4927
4928
4929 /**********************************************************************
4930  *              GetMenuItemRect    (USER32.@)
4931  *
4932  *      ATTENTION: Here, the returned values in rect are the screen
4933  *                 coordinates of the item just like if the menu was
4934  *                 always on the upper left side of the application.
4935  *
4936  */
4937 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4938                                  LPRECT rect)
4939 {
4940      POPUPMENU *itemMenu;
4941      MENUITEM *item;
4942      HWND referenceHwnd;
4943
4944      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4945
4946      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4947      referenceHwnd = hwnd;
4948
4949      if(!hwnd)
4950      {
4951          itemMenu = MENU_GetMenu(hMenu);
4952          if (itemMenu == NULL)
4953              return FALSE;
4954
4955          if(itemMenu->hWnd == 0)
4956              return FALSE;
4957          referenceHwnd = itemMenu->hWnd;
4958      }
4959
4960      if ((rect == NULL) || (item == NULL))
4961          return FALSE;
4962
4963      *rect = item->rect;
4964
4965      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4966
4967      return TRUE;
4968 }
4969
4970 /**********************************************************************
4971  *              SetMenuInfo    (USER32.@)
4972  *
4973  * FIXME
4974  *      actually use the items to draw the menu
4975  *      (recalculate and/or redraw)
4976  */
4977 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
4978 {
4979     POPUPMENU *menu;
4980     if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
4981
4982     if (lpmi->fMask & MIM_BACKGROUND)
4983         menu->hbrBack = lpmi->hbrBack;
4984
4985     if (lpmi->fMask & MIM_HELPID)
4986         menu->dwContextHelpID = lpmi->dwContextHelpID;
4987
4988     if (lpmi->fMask & MIM_MAXHEIGHT)
4989         menu->cyMax = lpmi->cyMax;
4990
4991     if (lpmi->fMask & MIM_MENUDATA)
4992         menu->dwMenuData = lpmi->dwMenuData;
4993
4994     if (lpmi->fMask & MIM_STYLE)
4995         menu->dwStyle = lpmi->dwStyle;
4996
4997     if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
4998         int i;
4999         MENUITEM *item = menu->items;
5000         for( i = menu->nItems; i; i--, item++)
5001             if( item->fType & MF_POPUP)
5002                 menu_SetMenuInfo( item->hSubMenu, lpmi);
5003     }
5004     return TRUE;
5005 }
5006
5007 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5008 {
5009     TRACE("(%p %p)\n", hMenu, lpmi);
5010     if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5011         if( lpmi->fMask & MIM_STYLE) {
5012             if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5013             if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5014             if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5015         }
5016         return TRUE;
5017     }
5018     SetLastError( ERROR_INVALID_PARAMETER);
5019     return FALSE;
5020 }
5021
5022 /**********************************************************************
5023  *              GetMenuInfo    (USER32.@)
5024  *
5025  *  NOTES
5026  *      win98/NT5.0
5027  *
5028  */
5029 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5030 {   POPUPMENU *menu;
5031
5032     TRACE("(%p %p)\n", hMenu, lpmi);
5033
5034     if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5035     {
5036
5037         if (lpmi->fMask & MIM_BACKGROUND)
5038             lpmi->hbrBack = menu->hbrBack;
5039
5040         if (lpmi->fMask & MIM_HELPID)
5041             lpmi->dwContextHelpID = menu->dwContextHelpID;
5042
5043         if (lpmi->fMask & MIM_MAXHEIGHT)
5044             lpmi->cyMax = menu->cyMax;
5045
5046         if (lpmi->fMask & MIM_MENUDATA)
5047             lpmi->dwMenuData = menu->dwMenuData;
5048
5049         if (lpmi->fMask & MIM_STYLE)
5050             lpmi->dwStyle = menu->dwStyle;
5051
5052         return TRUE;
5053     }
5054     SetLastError( ERROR_INVALID_PARAMETER);
5055     return FALSE;
5056 }
5057
5058
5059 /**********************************************************************
5060  *         SetMenuContextHelpId    (USER32.@)
5061  */
5062 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5063 {
5064     LPPOPUPMENU menu;
5065
5066     TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5067
5068     if ((menu = MENU_GetMenu(hMenu)))
5069     {
5070         menu->dwContextHelpID = dwContextHelpID;
5071         return TRUE;
5072     }
5073     return FALSE;
5074 }
5075
5076
5077 /**********************************************************************
5078  *         GetMenuContextHelpId    (USER32.@)
5079  */
5080 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5081 {
5082     LPPOPUPMENU menu;
5083
5084     TRACE("(%p)\n", hMenu);
5085
5086     if ((menu = MENU_GetMenu(hMenu)))
5087     {
5088         return menu->dwContextHelpID;
5089     }
5090     return 0;
5091 }
5092
5093 /**********************************************************************
5094  *         MenuItemFromPoint    (USER32.@)
5095  */
5096 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5097 {
5098     POPUPMENU *menu = MENU_GetMenu(hMenu);
5099     UINT pos;
5100
5101     /*FIXME: Do we have to handle hWnd here? */
5102     if (!menu) return -1;
5103     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5104     return pos;
5105 }
5106
5107
5108 /**********************************************************************
5109  *           translate_accelerator
5110  */
5111 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5112                                    BYTE fVirt, WORD key, WORD cmd )
5113 {
5114     INT mask = 0;
5115     UINT mesg = 0;
5116
5117     if (wParam != key) return FALSE;
5118
5119     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5120     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5121     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5122
5123     if (message == WM_CHAR || message == WM_SYSCHAR)
5124     {
5125         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5126         {
5127             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5128             goto found;
5129         }
5130     }
5131     else
5132     {
5133         if(fVirt & FVIRTKEY)
5134         {
5135             TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5136                           wParam, 0xff & HIWORD(lParam));
5137
5138             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5139             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5140         }
5141         else
5142         {
5143             if (!(lParam & 0x01000000))  /* no special_key */
5144             {
5145                 if ((fVirt & FALT) && (lParam & 0x20000000))
5146                 {                              /* ^^ ALT pressed */
5147                     TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5148                     goto found;
5149                 }
5150             }
5151         }
5152     }
5153     return FALSE;
5154
5155  found:
5156     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5157         mesg = 1;
5158     else
5159     {
5160         HMENU hMenu, hSubMenu, hSysMenu;
5161         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5162
5163         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5164         hSysMenu = get_win_sys_menu( hWnd );
5165
5166         /* find menu item and ask application to initialize it */
5167         /* 1. in the system menu */
5168         hSubMenu = hSysMenu;
5169         nPos = cmd;
5170         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5171         {
5172             if (GetCapture())
5173                 mesg = 2;
5174             if (!IsWindowEnabled(hWnd))
5175                 mesg = 3;
5176             else
5177             {
5178                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5179                 if(hSubMenu != hSysMenu)
5180                 {
5181                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5182                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5183                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5184                 }
5185                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5186             }
5187         }
5188         else /* 2. in the window's menu */
5189         {
5190             hSubMenu = hMenu;
5191             nPos = cmd;
5192             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5193             {
5194                 if (GetCapture())
5195                     mesg = 2;
5196                 if (!IsWindowEnabled(hWnd))
5197                     mesg = 3;
5198                 else
5199                 {
5200                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5201                     if(hSubMenu != hMenu)
5202                     {
5203                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5204                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5205                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5206                     }
5207                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5208                 }
5209             }
5210         }
5211
5212         if (mesg == 0)
5213         {
5214             if (uSysStat != (UINT)-1)
5215             {
5216                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5217                     mesg=4;
5218                 else
5219                     mesg=WM_SYSCOMMAND;
5220             }
5221             else
5222             {
5223                 if (uStat != (UINT)-1)
5224                 {
5225                     if (IsIconic(hWnd))
5226                         mesg=5;
5227                     else
5228                     {
5229                         if (uStat & (MF_DISABLED|MF_GRAYED))
5230                             mesg=6;
5231                         else
5232                             mesg=WM_COMMAND;
5233                     }
5234                 }
5235                 else
5236                     mesg=WM_COMMAND;
5237             }
5238         }
5239     }
5240
5241     if( mesg==WM_COMMAND )
5242     {
5243         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5244         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5245     }
5246     else if( mesg==WM_SYSCOMMAND )
5247     {
5248         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5249         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5250     }
5251     else
5252     {
5253         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5254          *   #0: unknown (please report!)
5255          *   #1: for WM_KEYUP,WM_SYSKEYUP
5256          *   #2: mouse is captured
5257          *   #3: window is disabled
5258          *   #4: it's a disabled system menu option
5259          *   #5: it's a menu option, but window is iconic
5260          *   #6: it's a menu option, but disabled
5261          */
5262         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5263         if(mesg==0)
5264             ERR_(accel)(" unknown reason - please report!\n");
5265     }
5266     return TRUE;
5267 }
5268
5269 /**********************************************************************
5270  *      TranslateAcceleratorA     (USER32.@)
5271  *      TranslateAccelerator      (USER32.@)
5272  */
5273 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5274 {
5275     switch (msg->message)
5276     {
5277     case WM_KEYDOWN:
5278     case WM_SYSKEYDOWN:
5279         return TranslateAcceleratorW( hWnd, hAccel, msg );
5280
5281     case WM_CHAR:
5282     case WM_SYSCHAR:
5283         {
5284             MSG msgW = *msg;
5285             char ch = LOWORD(msg->wParam);
5286             WCHAR wch;
5287             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5288             msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5289             return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5290         }
5291
5292     default:
5293         return 0;
5294     }
5295 }
5296
5297 /**********************************************************************
5298  *      TranslateAcceleratorW     (USER32.@)
5299  */
5300 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5301 {
5302     ACCEL data[32], *ptr = data;
5303     int i, count;
5304
5305     if (!hWnd) return 0;
5306
5307     if (msg->message != WM_KEYDOWN &&
5308         msg->message != WM_SYSKEYDOWN &&
5309         msg->message != WM_CHAR &&
5310         msg->message != WM_SYSCHAR)
5311         return 0;
5312
5313     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5314                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5315
5316     if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5317     if (count > sizeof(data)/sizeof(data[0]))
5318     {
5319         if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5320     }
5321     count = CopyAcceleratorTableW( hAccel, ptr, count );
5322     for (i = 0; i < count; i++)
5323     {
5324         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5325                                    ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5326             break;
5327     }
5328     if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5329     return (i < count);
5330 }