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