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