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