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