d3d9: Properly destroy vertex declarations.
[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
3476     case WM_PRINTCLIENT:
3477         {
3478             MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3479                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3480             return 0;
3481         }
3482
3483     case WM_ERASEBKGND:
3484         return 1;
3485
3486     case WM_DESTROY:
3487         /* zero out global pointer in case resident popup window was destroyed. */
3488         if (hwnd == top_popup) {
3489             top_popup = 0;
3490             top_popup_hmenu = NULL;
3491         }
3492         break;
3493
3494     case WM_SHOWWINDOW:
3495
3496         if( wParam )
3497         {
3498             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3499         }
3500         else
3501             SetWindowLongPtrW( hwnd, 0, 0 );
3502         break;
3503
3504     case MM_SETMENUHANDLE:
3505         SetWindowLongPtrW( hwnd, 0, wParam );
3506         break;
3507
3508     case MM_GETMENUHANDLE:
3509         return GetWindowLongPtrW( hwnd, 0 );
3510
3511     default:
3512         return DefWindowProcW( hwnd, message, wParam, lParam );
3513     }
3514     return 0;
3515 }
3516
3517
3518 /***********************************************************************
3519  *           MENU_GetMenuBarHeight
3520  *
3521  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3522  */
3523 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3524                               INT orgX, INT orgY )
3525 {
3526     HDC hdc;
3527     RECT rectBar;
3528     LPPOPUPMENU lppop;
3529
3530     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3531
3532     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3533
3534     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3535     SelectObject( hdc, get_menu_font(FALSE));
3536     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3537     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3538     ReleaseDC( hwnd, hdc );
3539     return lppop->Height;
3540 }
3541
3542
3543 /*******************************************************************
3544  *         ChangeMenuA    (USER32.@)
3545  */
3546 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3547                              UINT id, UINT flags )
3548 {
3549     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3550     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3551                                                  id, data );
3552     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3553     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3554                                                 id, data );
3555     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3556                                               flags & MF_BYPOSITION ? pos : id,
3557                                               flags & ~MF_REMOVE );
3558     /* Default: MF_INSERT */
3559     return InsertMenuA( hMenu, pos, flags, id, data );
3560 }
3561
3562
3563 /*******************************************************************
3564  *         ChangeMenuW    (USER32.@)
3565  */
3566 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3567                              UINT id, UINT flags )
3568 {
3569     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3570     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3571                                                  id, data );
3572     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3573     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3574                                                 id, data );
3575     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3576                                               flags & MF_BYPOSITION ? pos : id,
3577                                               flags & ~MF_REMOVE );
3578     /* Default: MF_INSERT */
3579     return InsertMenuW( hMenu, pos, flags, id, data );
3580 }
3581
3582
3583 /*******************************************************************
3584  *         CheckMenuItem    (USER32.@)
3585  */
3586 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3587 {
3588     MENUITEM *item;
3589     DWORD ret;
3590
3591     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3592     ret = item->fState & MF_CHECKED;
3593     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3594     else item->fState &= ~MF_CHECKED;
3595     return ret;
3596 }
3597
3598
3599 /**********************************************************************
3600  *         EnableMenuItem    (USER32.@)
3601  */
3602 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3603 {
3604     UINT    oldflags;
3605     MENUITEM *item;
3606     POPUPMENU *menu;
3607
3608     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3609
3610     /* Get the Popupmenu to access the owner menu */
3611     if (!(menu = MENU_GetMenu(hMenu)))
3612         return (UINT)-1;
3613
3614     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3615         return (UINT)-1;
3616
3617     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3618     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3619
3620     /* If the close item in the system menu change update the close button */
3621     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3622     {
3623         if (menu->hSysMenuOwner != 0)
3624         {
3625             RECT rc;
3626             POPUPMENU* parentMenu;
3627
3628             /* Get the parent menu to access*/
3629             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3630                 return (UINT)-1;
3631
3632             /* Refresh the frame to reflect the change */
3633             GetWindowRect(parentMenu->hWnd, &rc);
3634             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3635             rc.bottom = 0;
3636             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3637         }
3638     }
3639
3640     return oldflags;
3641 }
3642
3643
3644 /*******************************************************************
3645  *         GetMenuStringA    (USER32.@)
3646  */
3647 INT WINAPI GetMenuStringA(
3648         HMENU hMenu,    /* [in] menuhandle */
3649         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3650         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3651         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3652         UINT wFlags     /* [in] MF_ flags */
3653 ) {
3654     MENUITEM *item;
3655
3656     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3657     if (str && nMaxSiz) str[0] = '\0';
3658     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3659         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3660         return 0;
3661     }
3662     if (!item->text) return 0;
3663     if (!str || !nMaxSiz) return strlenW(item->text);
3664     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3665         str[nMaxSiz-1] = 0;
3666     TRACE("returning %s\n", debugstr_a(str));
3667     return strlen(str);
3668 }
3669
3670
3671 /*******************************************************************
3672  *         GetMenuStringW    (USER32.@)
3673  */
3674 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3675                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3676 {
3677     MENUITEM *item;
3678
3679     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3680     if (str && nMaxSiz) str[0] = '\0';
3681     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3682         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3683         return 0;
3684     }
3685     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3686     if( !(item->text)) {
3687         str[0] = 0;
3688         return 0;
3689     }
3690     lstrcpynW( str, item->text, nMaxSiz );
3691     TRACE("returning %s\n", debugstr_w(str));
3692     return strlenW(str);
3693 }
3694
3695
3696 /**********************************************************************
3697  *         HiliteMenuItem    (USER32.@)
3698  */
3699 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3700                                 UINT wHilite )
3701 {
3702     LPPOPUPMENU menu;
3703     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3704     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3705     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3706     if (menu->FocusedItem == wItemID) return TRUE;
3707     MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3708     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3709     return TRUE;
3710 }
3711
3712
3713 /**********************************************************************
3714  *         GetMenuState    (USER32.@)
3715  */
3716 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3717 {
3718     MENUITEM *item;
3719     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3720     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3721     debug_print_menuitem ("  item: ", item, "");
3722     if (item->fType & MF_POPUP)
3723     {
3724         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3725         if (!menu) return -1;
3726         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3727     }
3728     else
3729     {
3730         /* We used to (from way back then) mask the result to 0xff.  */
3731         /* I don't know why and it seems wrong as the documented */
3732         /* return flag MF_SEPARATOR is outside that mask.  */
3733         return (item->fType | item->fState);
3734     }
3735 }
3736
3737
3738 /**********************************************************************
3739  *         GetMenuItemCount    (USER32.@)
3740  */
3741 INT WINAPI GetMenuItemCount( HMENU hMenu )
3742 {
3743     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3744     if (!menu) return -1;
3745     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3746     return menu->nItems;
3747 }
3748
3749
3750 /**********************************************************************
3751  *         GetMenuItemID    (USER32.@)
3752  */
3753 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3754 {
3755     MENUITEM * lpmi;
3756
3757     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3758     if (lpmi->fType & MF_POPUP) return -1;
3759     return lpmi->wID;
3760
3761 }
3762
3763
3764 /**********************************************************************
3765  *         MENU_mnu2mnuii
3766  *
3767  * Uses flags, id and text ptr, passed by InsertMenu() and
3768  * ModifyMenu() to setup a MenuItemInfo structure.
3769  */
3770 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3771         LPMENUITEMINFOW pmii)
3772 {
3773     ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3774     pmii->cbSize = sizeof( MENUITEMINFOW);
3775     pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3776     /* setting bitmap clears text and vice versa */
3777     if( IS_STRING_ITEM(flags)) {
3778         pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3779         if( !str)
3780             flags |= MF_SEPARATOR;
3781         /* Item beginning with a backspace is a help item */
3782         /* FIXME: wrong place, this is only true in win16 */
3783         else if( *str == '\b') {
3784             flags |= MF_HELP;
3785             str++;
3786         }
3787         pmii->dwTypeData = (LPWSTR)str;
3788     } else if( flags & MFT_BITMAP){
3789         pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3790         pmii->hbmpItem = HBITMAP_32(LOWORD(str));
3791     }
3792     if( flags & MF_OWNERDRAW){
3793         pmii->fMask |= MIIM_DATA;
3794         pmii->dwItemData = (ULONG_PTR) str;
3795     }
3796     if( flags & MF_POPUP) {
3797         pmii->fMask |= MIIM_SUBMENU;
3798         pmii->hSubMenu = (HMENU)id;
3799     }
3800     if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3801     pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3802     pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3803     pmii->wID = (UINT)id;
3804 }
3805
3806
3807 /*******************************************************************
3808  *         InsertMenuW    (USER32.@)
3809  */
3810 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3811                          UINT_PTR id, LPCWSTR str )
3812 {
3813     MENUITEM *item;
3814     MENUITEMINFOW mii;
3815
3816     if (IS_STRING_ITEM(flags) && str)
3817         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3818               hMenu, pos, flags, id, debugstr_w(str) );
3819     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3820                hMenu, pos, flags, id, str );
3821
3822     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3823     MENU_mnu2mnuii( flags, id, str, &mii);
3824     if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3825     {
3826         RemoveMenu( hMenu, pos, flags );
3827         return FALSE;
3828     }
3829
3830     item->hCheckBit = item->hUnCheckBit = 0;
3831     return TRUE;
3832 }
3833
3834
3835 /*******************************************************************
3836  *         InsertMenuA    (USER32.@)
3837  */
3838 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3839                          UINT_PTR id, LPCSTR str )
3840 {
3841     BOOL ret = FALSE;
3842
3843     if (IS_STRING_ITEM(flags) && str)
3844     {
3845         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3846         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3847         if (newstr)
3848         {
3849             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3850             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3851             HeapFree( GetProcessHeap(), 0, newstr );
3852         }
3853         return ret;
3854     }
3855     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3856 }
3857
3858
3859 /*******************************************************************
3860  *         AppendMenuA    (USER32.@)
3861  */
3862 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3863                          UINT_PTR id, LPCSTR data )
3864 {
3865     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3866 }
3867
3868
3869 /*******************************************************************
3870  *         AppendMenuW    (USER32.@)
3871  */
3872 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3873                          UINT_PTR id, LPCWSTR data )
3874 {
3875     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3876 }
3877
3878
3879 /**********************************************************************
3880  *         RemoveMenu    (USER32.@)
3881  */
3882 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3883 {
3884     LPPOPUPMENU menu;
3885     MENUITEM *item;
3886
3887     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3888     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3889     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3890
3891       /* Remove item */
3892
3893     MENU_FreeItemData( item );
3894
3895     if (--menu->nItems == 0)
3896     {
3897         HeapFree( GetProcessHeap(), 0, menu->items );
3898         menu->items = NULL;
3899     }
3900     else
3901     {
3902         while(nPos < menu->nItems)
3903         {
3904             *item = *(item+1);
3905             item++;
3906             nPos++;
3907         }
3908         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3909                                    menu->nItems * sizeof(MENUITEM) );
3910     }
3911     return TRUE;
3912 }
3913
3914
3915 /**********************************************************************
3916  *         DeleteMenu    (USER32.@)
3917  */
3918 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3919 {
3920     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3921     if (!item) return FALSE;
3922     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3923       /* nPos is now the position of the item */
3924     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3925     return TRUE;
3926 }
3927
3928
3929 /*******************************************************************
3930  *         ModifyMenuW    (USER32.@)
3931  */
3932 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3933                          UINT_PTR id, LPCWSTR str )
3934 {
3935     MENUITEM *item;
3936     MENUITEMINFOW mii;
3937
3938     if (IS_STRING_ITEM(flags))
3939         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3940     else
3941         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3942
3943     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3944     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3945     MENU_mnu2mnuii( flags, id, str, &mii);
3946     return SetMenuItemInfo_common( item, &mii, TRUE);
3947 }
3948
3949
3950 /*******************************************************************
3951  *         ModifyMenuA    (USER32.@)
3952  */
3953 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3954                          UINT_PTR id, LPCSTR str )
3955 {
3956     BOOL ret = FALSE;
3957
3958     if (IS_STRING_ITEM(flags) && str)
3959     {
3960         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3961         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3962         if (newstr)
3963         {
3964             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3965             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3966             HeapFree( GetProcessHeap(), 0, newstr );
3967         }
3968         return ret;
3969     }
3970     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3971 }
3972
3973
3974 /**********************************************************************
3975  *         CreatePopupMenu    (USER32.@)
3976  */
3977 HMENU WINAPI CreatePopupMenu(void)
3978 {
3979     HMENU hmenu;
3980     POPUPMENU *menu;
3981
3982     if (!(hmenu = CreateMenu())) return 0;
3983     menu = MENU_GetMenu( hmenu );
3984     menu->wFlags |= MF_POPUP;
3985     menu->bTimeToHide = FALSE;
3986     return hmenu;
3987 }
3988
3989
3990 /**********************************************************************
3991  *         GetMenuCheckMarkDimensions    (USER.417)
3992  *         GetMenuCheckMarkDimensions    (USER32.@)
3993  */
3994 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3995 {
3996     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3997 }
3998
3999
4000 /**********************************************************************
4001  *         SetMenuItemBitmaps    (USER32.@)
4002  */
4003 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4004                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4005 {
4006     MENUITEM *item;
4007
4008     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4009
4010     if (!hNewCheck && !hNewUnCheck)
4011     {
4012         item->fState &= ~MF_USECHECKBITMAPS;
4013     }
4014     else  /* Install new bitmaps */
4015     {
4016         item->hCheckBit = hNewCheck;
4017         item->hUnCheckBit = hNewUnCheck;
4018         item->fState |= MF_USECHECKBITMAPS;
4019     }
4020     return TRUE;
4021 }
4022
4023
4024 /**********************************************************************
4025  *         CreateMenu    (USER32.@)
4026  */
4027 HMENU WINAPI CreateMenu(void)
4028 {
4029     HMENU hMenu;
4030     LPPOPUPMENU menu;
4031     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
4032     menu = USER_HEAP_LIN_ADDR(hMenu);
4033
4034     ZeroMemory(menu, sizeof(POPUPMENU));
4035     menu->wMagic = MENU_MAGIC;
4036     menu->FocusedItem = NO_SELECTED_ITEM;
4037     menu->bTimeToHide = FALSE;
4038
4039     TRACE("return %p\n", hMenu );
4040
4041     return hMenu;
4042 }
4043
4044
4045 /**********************************************************************
4046  *         DestroyMenu    (USER32.@)
4047  */
4048 BOOL WINAPI DestroyMenu( HMENU hMenu )
4049 {
4050     LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
4051
4052     TRACE("(%p)\n", hMenu);
4053
4054
4055     if (!lppop) return FALSE;
4056
4057     lppop->wMagic = 0;  /* Mark it as destroyed */
4058
4059     /* DestroyMenu should not destroy system menu popup owner */
4060     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4061     {
4062         DestroyWindow( lppop->hWnd );
4063         lppop->hWnd = 0;
4064     }
4065
4066     if (lppop->items) /* recursively destroy submenus */
4067     {
4068         int i;
4069         MENUITEM *item = lppop->items;
4070         for (i = lppop->nItems; i > 0; i--, item++)
4071         {
4072             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4073             MENU_FreeItemData( item );
4074         }
4075         HeapFree( GetProcessHeap(), 0, lppop->items );
4076     }
4077     USER_HEAP_FREE( hMenu );
4078     return TRUE;
4079 }
4080
4081
4082 /**********************************************************************
4083  *         GetSystemMenu    (USER32.@)
4084  */
4085 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4086 {
4087     WND *wndPtr = WIN_GetPtr( hWnd );
4088     HMENU retvalue = 0;
4089
4090     if (wndPtr == WND_DESKTOP) return 0;
4091     if (wndPtr == WND_OTHER_PROCESS)
4092     {
4093         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4094     }
4095     else if (wndPtr)
4096     {
4097         if (wndPtr->hSysMenu && bRevert)
4098         {
4099             DestroyMenu(wndPtr->hSysMenu);
4100             wndPtr->hSysMenu = 0;
4101         }
4102
4103         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4104             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4105
4106         if( wndPtr->hSysMenu )
4107         {
4108             POPUPMENU *menu;
4109             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4110
4111             /* Store the dummy sysmenu handle to facilitate the refresh */
4112             /* of the close button if the SC_CLOSE item change */
4113             menu = MENU_GetMenu(retvalue);
4114             if ( menu )
4115                menu->hSysMenuOwner = wndPtr->hSysMenu;
4116         }
4117         WIN_ReleasePtr( wndPtr );
4118     }
4119     return bRevert ? 0 : retvalue;
4120 }
4121
4122
4123 /*******************************************************************
4124  *         SetSystemMenu    (USER32.@)
4125  */
4126 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4127 {
4128     WND *wndPtr = WIN_GetPtr( hwnd );
4129
4130     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4131     {
4132         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4133         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4134         WIN_ReleasePtr( wndPtr );
4135         return TRUE;
4136     }
4137     return FALSE;
4138 }
4139
4140
4141 /**********************************************************************
4142  *         GetMenu    (USER32.@)
4143  */
4144 HMENU WINAPI GetMenu( HWND hWnd )
4145 {
4146     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4147     TRACE("for %p returning %p\n", hWnd, retvalue);
4148     return retvalue;
4149 }
4150
4151 /**********************************************************************
4152  *         GetMenuBarInfo    (USER32.@)
4153  */
4154 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4155 {
4156     FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4157     return FALSE;
4158 }
4159
4160 /**********************************************************************
4161  *         MENU_SetMenu
4162  *
4163  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4164  * SetWindowPos call that would result if SetMenu were called directly.
4165  */
4166 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4167 {
4168     TRACE("(%p, %p);\n", hWnd, hMenu);
4169
4170     if (hMenu && !IsMenu(hMenu))
4171     {
4172         WARN("hMenu %p is not a menu handle\n", hMenu);
4173         return FALSE;
4174     }
4175     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4176         return FALSE;
4177
4178     hWnd = WIN_GetFullHandle( hWnd );
4179     if (GetCapture() == hWnd)
4180         set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
4181
4182     if (hMenu != 0)
4183     {
4184         LPPOPUPMENU lpmenu;
4185
4186         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4187
4188         lpmenu->hWnd = hWnd;
4189         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4190     }
4191     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4192     return TRUE;
4193 }
4194
4195
4196 /**********************************************************************
4197  *         SetMenu    (USER32.@)
4198  */
4199 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4200 {   
4201     if(!MENU_SetMenu(hWnd, hMenu))
4202         return FALSE;
4203  
4204     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4205                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4206     return TRUE;
4207 }
4208
4209
4210 /**********************************************************************
4211  *         GetSubMenu    (USER32.@)
4212  */
4213 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4214 {
4215     MENUITEM * lpmi;
4216
4217     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4218     if (!(lpmi->fType & MF_POPUP)) return 0;
4219     return lpmi->hSubMenu;
4220 }
4221
4222
4223 /**********************************************************************
4224  *         DrawMenuBar    (USER32.@)
4225  */
4226 BOOL WINAPI DrawMenuBar( HWND hWnd )
4227 {
4228     LPPOPUPMENU lppop;
4229     HMENU hMenu = GetMenu(hWnd);
4230
4231     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4232         return FALSE;
4233     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4234
4235     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4236     lppop->hwndOwner = hWnd;
4237     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4238                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4239     return TRUE;
4240 }
4241
4242 /***********************************************************************
4243  *           DrawMenuBarTemp   (USER32.@)
4244  *
4245  * UNDOCUMENTED !!
4246  *
4247  * called by W98SE desk.cpl Control Panel Applet
4248  *
4249  * Not 100% sure about the param names, but close.
4250  */
4251 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4252 {
4253     LPPOPUPMENU lppop;
4254     UINT i,retvalue;
4255     HFONT hfontOld = 0;
4256     BOOL flat_menu = FALSE;
4257
4258     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4259
4260     if (!hMenu)
4261         hMenu = GetMenu(hwnd);
4262
4263     if (!hFont)
4264         hFont = get_menu_font(FALSE);
4265
4266     lppop = MENU_GetMenu( hMenu );
4267     if (lppop == NULL || lprect == NULL)
4268     {
4269         retvalue = GetSystemMetrics(SM_CYMENU);
4270         goto END;
4271     }
4272
4273     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4274
4275     hfontOld = SelectObject( hDC, hFont);
4276
4277     if (lppop->Height == 0)
4278         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4279
4280     lprect->bottom = lprect->top + lppop->Height;
4281
4282     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4283
4284     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4285     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4286     LineTo( hDC, lprect->right, lprect->bottom );
4287
4288     if (lppop->nItems == 0)
4289     {
4290         retvalue = GetSystemMetrics(SM_CYMENU);
4291         goto END;
4292     }
4293
4294     for (i = 0; i < lppop->nItems; i++)
4295     {
4296         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4297                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4298     }
4299     retvalue = lppop->Height;
4300
4301 END:
4302     if (hfontOld) SelectObject (hDC, hfontOld);
4303     return retvalue;
4304 }
4305
4306 /***********************************************************************
4307  *           EndMenu   (USER.187)
4308  *           EndMenu   (USER32.@)
4309  */
4310 BOOL WINAPI EndMenu(void)
4311 {
4312     /* if we are in the menu code, and it is active */
4313     if (!fEndMenu && top_popup)
4314     {
4315         /* terminate the menu handling code */
4316         fEndMenu = TRUE;
4317
4318         /* needs to be posted to wakeup the internal menu handler */
4319         /* which will now terminate the menu, in the event that */
4320         /* the main window was minimized, or lost focus, so we */
4321         /* don't end up with an orphaned menu */
4322         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4323     }
4324     return fEndMenu;
4325 }
4326
4327
4328 /***********************************************************************
4329  *           LookupMenuHandle   (USER.217)
4330  */
4331 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4332 {
4333     HMENU hmenu32 = HMENU_32(hmenu);
4334     UINT id32 = id;
4335     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4336     else return HMENU_16(hmenu32);
4337 }
4338
4339
4340 /**********************************************************************
4341  *          LoadMenu    (USER.150)
4342  */
4343 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4344 {
4345     HRSRC16 hRsrc;
4346     HGLOBAL16 handle;
4347     HMENU16 hMenu;
4348
4349     if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4350     if (!name) return 0;
4351
4352     instance = GetExePtr( instance );
4353     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4354     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4355     hMenu = LoadMenuIndirect16(LockResource16(handle));
4356     FreeResource16( handle );
4357     return hMenu;
4358 }
4359
4360
4361 /*****************************************************************
4362  *        LoadMenuA   (USER32.@)
4363  */
4364 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4365 {
4366     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4367     if (!hrsrc) return 0;
4368     return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4369 }
4370
4371
4372 /*****************************************************************
4373  *        LoadMenuW   (USER32.@)
4374  */
4375 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4376 {
4377     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4378     if (!hrsrc) return 0;
4379     return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4380 }
4381
4382
4383 /**********************************************************************
4384  *          LoadMenuIndirect    (USER.220)
4385  */
4386 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4387 {
4388     HMENU hMenu;
4389     WORD version, offset;
4390     LPCSTR p = template;
4391
4392     TRACE("(%p)\n", template );
4393     version = GET_WORD(p);
4394     p += sizeof(WORD);
4395     if (version)
4396     {
4397         WARN("version must be 0 for Win16\n" );
4398         return 0;
4399     }
4400     offset = GET_WORD(p);
4401     p += sizeof(WORD) + offset;
4402     if (!(hMenu = CreateMenu())) return 0;
4403     if (!MENU_ParseResource( p, hMenu, FALSE ))
4404     {
4405         DestroyMenu( hMenu );
4406         return 0;
4407     }
4408     return HMENU_16(hMenu);
4409 }
4410
4411
4412 /**********************************************************************
4413  *          LoadMenuIndirectW    (USER32.@)
4414  */
4415 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4416 {
4417     HMENU hMenu;
4418     WORD version, offset;
4419     LPCSTR p = template;
4420
4421     version = GET_WORD(p);
4422     p += sizeof(WORD);
4423     TRACE("%p, ver %d\n", template, version );
4424     switch (version)
4425     {
4426       case 0: /* standard format is version of 0 */
4427         offset = GET_WORD(p);
4428         p += sizeof(WORD) + offset;
4429         if (!(hMenu = CreateMenu())) return 0;
4430         if (!MENU_ParseResource( p, hMenu, TRUE ))
4431           {
4432             DestroyMenu( hMenu );
4433             return 0;
4434           }
4435         return hMenu;
4436       case 1: /* extended format is version of 1 */
4437         offset = GET_WORD(p);
4438         p += sizeof(WORD) + offset;
4439         if (!(hMenu = CreateMenu())) return 0;
4440         if (!MENUEX_ParseResource( p, hMenu))
4441           {
4442             DestroyMenu( hMenu );
4443             return 0;
4444           }
4445         return hMenu;
4446       default:
4447         ERR("version %d not supported.\n", version);
4448         return 0;
4449     }
4450 }
4451
4452
4453 /**********************************************************************
4454  *          LoadMenuIndirectA    (USER32.@)
4455  */
4456 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4457 {
4458     return LoadMenuIndirectW( template );
4459 }
4460
4461
4462 /**********************************************************************
4463  *              IsMenu    (USER32.@)
4464  */
4465 BOOL WINAPI IsMenu(HMENU hmenu)
4466 {
4467     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4468
4469     if (!menu)
4470     {
4471         SetLastError(ERROR_INVALID_MENU_HANDLE);
4472         return FALSE;
4473     }
4474     return TRUE;
4475 }
4476
4477 /**********************************************************************
4478  *              GetMenuItemInfo_common
4479  */
4480
4481 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4482                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4483 {
4484     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4485
4486     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4487
4488     if (!menu) {
4489         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4490         return FALSE;
4491     }
4492     
4493     if( lpmii->fMask & MIIM_TYPE) {
4494         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4495             WARN("invalid combination of fMask bits used\n");
4496             /* this does not happen on Win9x/ME */
4497             SetLastError( ERROR_INVALID_PARAMETER);
4498             return FALSE;
4499         }
4500         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4501         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4502         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4503         if( lpmii->fType & MFT_BITMAP) {
4504             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4505             lpmii->cch = 0;
4506         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4507             /* this does not happen on Win9x/ME */
4508             lpmii->dwTypeData = 0;
4509             lpmii->cch = 0;
4510         }
4511     }
4512
4513     /* copy the text string */
4514     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4515          if( !menu->text ) {
4516                 if(lpmii->dwTypeData && lpmii->cch) {
4517                     lpmii->cch = 0;
4518                     if( unicode)
4519                         *((WCHAR *)lpmii->dwTypeData) = 0;
4520                     else
4521                         *((CHAR *)lpmii->dwTypeData) = 0;
4522                 }
4523          } else {
4524             int len;
4525             if (unicode)
4526             {
4527                 len = strlenW(menu->text);
4528                 if(lpmii->dwTypeData && lpmii->cch)
4529                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4530             }
4531             else
4532             {
4533                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4534                         0, NULL, NULL ) - 1;
4535                 if(lpmii->dwTypeData && lpmii->cch)
4536                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4537                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4538                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4539             }
4540             /* if we've copied a substring we return its length */
4541             if(lpmii->dwTypeData && lpmii->cch)
4542                 if (lpmii->cch <= len + 1)
4543                     lpmii->cch--;
4544                 else
4545                     lpmii->cch = len;
4546             else {
4547                 /* return length of string */
4548                 /* not on Win9x/ME if fType & MFT_BITMAP */
4549                 lpmii->cch = len;
4550             }
4551         }
4552     }
4553
4554     if (lpmii->fMask & MIIM_FTYPE)
4555         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4556
4557     if (lpmii->fMask & MIIM_BITMAP)
4558         lpmii->hbmpItem = menu->hbmpItem;
4559
4560     if (lpmii->fMask & MIIM_STATE)
4561         lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4562
4563     if (lpmii->fMask & MIIM_ID)
4564         lpmii->wID = menu->wID;
4565
4566     if (lpmii->fMask & MIIM_SUBMENU)
4567         lpmii->hSubMenu = menu->hSubMenu;
4568     else {
4569         /* hSubMenu is always cleared 
4570          * (not on Win9x/ME ) */
4571         lpmii->hSubMenu = 0;
4572     }
4573
4574     if (lpmii->fMask & MIIM_CHECKMARKS) {
4575         lpmii->hbmpChecked = menu->hCheckBit;
4576         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4577     }
4578     if (lpmii->fMask & MIIM_DATA)
4579         lpmii->dwItemData = menu->dwItemData;
4580
4581   return TRUE;
4582 }
4583
4584 /**********************************************************************
4585  *              GetMenuItemInfoA    (USER32.@)
4586  */
4587 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4588                                   LPMENUITEMINFOA lpmii)
4589 {
4590     BOOL ret;
4591     MENUITEMINFOA mii;
4592     if( lpmii->cbSize != sizeof( mii) &&
4593             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4594         SetLastError( ERROR_INVALID_PARAMETER);
4595         return FALSE;
4596     }
4597     memcpy( &mii, lpmii, lpmii->cbSize);
4598     mii.cbSize = sizeof( mii);
4599     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4600                                     (LPMENUITEMINFOW)&mii, FALSE);
4601     mii.cbSize = lpmii->cbSize;
4602     memcpy( lpmii, &mii, mii.cbSize);
4603     return ret;
4604 }
4605
4606 /**********************************************************************
4607  *              GetMenuItemInfoW    (USER32.@)
4608  */
4609 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4610                                   LPMENUITEMINFOW lpmii)
4611 {
4612     BOOL ret;
4613     MENUITEMINFOW mii;
4614     if( lpmii->cbSize != sizeof( mii) &&
4615             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4616         SetLastError( ERROR_INVALID_PARAMETER);
4617         return FALSE;
4618     }
4619     memcpy( &mii, lpmii, lpmii->cbSize);
4620     mii.cbSize = sizeof( mii);
4621     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4622     mii.cbSize = lpmii->cbSize;
4623     memcpy( lpmii, &mii, mii.cbSize);
4624     return ret;
4625 }
4626
4627
4628 /* set a menu item text from a ASCII or Unicode string */
4629 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4630 {
4631     if (!text)
4632         menu->text = NULL;
4633     else if (unicode)
4634     {
4635         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4636             strcpyW( menu->text, text );
4637     }
4638     else
4639     {
4640         LPCSTR str = (LPCSTR)text;
4641         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4642         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4643             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4644     }
4645 }
4646
4647
4648 /**********************************************************************
4649  *              MENU_depth
4650  *
4651  * detect if there are loops in the menu tree (or the depth is too large)
4652  */
4653 static int MENU_depth( POPUPMENU *pmenu, int depth)
4654 {
4655     int i;
4656     MENUITEM *item;
4657     int subdepth;
4658
4659     depth++;
4660     if( depth > MAXMENUDEPTH) return depth;
4661     item = pmenu->items;
4662     subdepth = depth;
4663     for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4664         POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4665         if( psubmenu){
4666             int bdepth = MENU_depth( psubmenu, depth);
4667             if( bdepth > subdepth) subdepth = bdepth;
4668         }
4669         if( subdepth > MAXMENUDEPTH)
4670             TRACE("<- hmenu %p\n", item->hSubMenu);
4671     }
4672     return subdepth;
4673 }
4674
4675
4676 /**********************************************************************
4677  *              SetMenuItemInfo_common
4678  *
4679  * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4680  * MIIM_BITMAP and MIIM_STRING flags instead.
4681  */
4682
4683 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4684                                        const MENUITEMINFOW *lpmii,
4685                                        BOOL unicode)
4686 {
4687     if (!menu) return FALSE;
4688
4689     debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4690
4691     if (lpmii->fMask & MIIM_FTYPE ) {
4692         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4693         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4694     }
4695     if (lpmii->fMask & MIIM_STRING ) {
4696         /* free the string when used */
4697         HeapFree(GetProcessHeap(), 0, menu->text);
4698         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4699     }
4700
4701     if (lpmii->fMask & MIIM_STATE)
4702          /* Other menu items having MFS_DEFAULT are not converted
4703            to normal items */
4704          menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4705
4706     if (lpmii->fMask & MIIM_ID)
4707         menu->wID = lpmii->wID;
4708
4709     if (lpmii->fMask & MIIM_SUBMENU) {
4710         menu->hSubMenu = lpmii->hSubMenu;
4711         if (menu->hSubMenu) {
4712             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4713             if (subMenu) {
4714                 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4715                     ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4716                     menu->hSubMenu = 0;
4717                     return FALSE;
4718                 }
4719                 subMenu->wFlags |= MF_POPUP;
4720                 menu->fType |= MF_POPUP;
4721             } else {
4722                 SetLastError( ERROR_INVALID_PARAMETER);
4723                 return FALSE;
4724             }
4725         }
4726         else
4727             menu->fType &= ~MF_POPUP;
4728     }
4729
4730     if (lpmii->fMask & MIIM_CHECKMARKS)
4731     {
4732         menu->hCheckBit = lpmii->hbmpChecked;
4733         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4734     }
4735     if (lpmii->fMask & MIIM_DATA)
4736         menu->dwItemData = lpmii->dwItemData;
4737
4738     if (lpmii->fMask & MIIM_BITMAP)
4739         menu->hbmpItem = lpmii->hbmpItem;
4740
4741     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4742         menu->fType |= MFT_SEPARATOR;
4743
4744     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4745     return TRUE;
4746 }
4747
4748 /**********************************************************************
4749  *              MENU_NormalizeMenuItemInfoStruct
4750  *
4751  * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4752  * check, copy and extend the MENUITEMINFO struct from the version that the application
4753  * supplied to the version used by wine source. */
4754 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4755                                               MENUITEMINFOW *pmii_out )
4756 {
4757     /* do we recognize the size? */
4758     if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4759             pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4760         SetLastError( ERROR_INVALID_PARAMETER);
4761         return FALSE;
4762     }
4763     /* copy the fields that we have */
4764     memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4765     /* if the hbmpItem member is missing then extend */
4766     if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4767         pmii_out->cbSize = sizeof( MENUITEMINFOW);
4768         pmii_out->hbmpItem = NULL;
4769     }
4770     /* test for invalid bit combinations */
4771     if( (pmii_out->fMask & MIIM_TYPE &&
4772          pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4773         (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4774         WARN("invalid combination of fMask bits used\n");
4775         /* this does not happen on Win9x/ME */
4776         SetLastError( ERROR_INVALID_PARAMETER);
4777         return FALSE;
4778     }
4779     /* convert old style (MIIM_TYPE) to the new */
4780     if( pmii_out->fMask & MIIM_TYPE){
4781         pmii_out->fMask |= MIIM_FTYPE;
4782         if( IS_STRING_ITEM(pmii_out->fType)){
4783             pmii_out->fMask |= MIIM_STRING;
4784         } else if( (pmii_out->fType) & MFT_BITMAP){
4785             pmii_out->fMask |= MIIM_BITMAP;
4786             pmii_out->hbmpItem = HBITMAP_32(LOWORD(pmii_out->dwTypeData));
4787         }
4788     }
4789     return TRUE;
4790 }
4791
4792 /**********************************************************************
4793  *              SetMenuItemInfoA    (USER32.@)
4794  */
4795 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4796                                  const MENUITEMINFOA *lpmii)
4797 {
4798     MENUITEMINFOW mii;
4799
4800     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4801
4802     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4803
4804     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4805                                   &mii, FALSE);
4806 }
4807
4808 /**********************************************************************
4809  *              SetMenuItemInfoW    (USER32.@)
4810  */
4811 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4812                                  const MENUITEMINFOW *lpmii)
4813 {
4814     MENUITEMINFOW mii;
4815
4816     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4817
4818     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4819     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4820                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4821 }
4822
4823 /**********************************************************************
4824  *              SetMenuDefaultItem    (USER32.@)
4825  *
4826  */
4827 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4828 {
4829         UINT i;
4830         POPUPMENU *menu;
4831         MENUITEM *item;
4832
4833         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4834
4835         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4836
4837         /* reset all default-item flags */
4838         item = menu->items;
4839         for (i = 0; i < menu->nItems; i++, item++)
4840         {
4841             item->fState &= ~MFS_DEFAULT;
4842         }
4843
4844         /* no default item */
4845         if ( -1 == uItem)
4846         {
4847             return TRUE;
4848         }
4849
4850         item = menu->items;
4851         if ( bypos )
4852         {
4853             if ( uItem >= menu->nItems ) return FALSE;
4854             item[uItem].fState |= MFS_DEFAULT;
4855             return TRUE;
4856         }
4857         else
4858         {
4859             for (i = 0; i < menu->nItems; i++, item++)
4860             {
4861                 if (item->wID == uItem)
4862                 {
4863                      item->fState |= MFS_DEFAULT;
4864                      return TRUE;
4865                 }
4866             }
4867
4868         }
4869         return FALSE;
4870 }
4871
4872 /**********************************************************************
4873  *              GetMenuDefaultItem    (USER32.@)
4874  */
4875 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4876 {
4877         POPUPMENU *menu;
4878         MENUITEM * item;
4879         UINT i = 0;
4880
4881         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4882
4883         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4884
4885         /* find default item */
4886         item = menu->items;
4887
4888         /* empty menu */
4889         if (! item) return -1;
4890
4891         while ( !( item->fState & MFS_DEFAULT ) )
4892         {
4893             i++; item++;
4894             if  (i >= menu->nItems ) return -1;
4895         }
4896
4897         /* default: don't return disabled items */
4898         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4899
4900         /* search rekursiv when needed */
4901         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4902         {
4903             UINT ret;
4904             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4905             if ( -1 != ret ) return ret;
4906
4907             /* when item not found in submenu, return the popup item */
4908         }
4909         return ( bypos ) ? i : item->wID;
4910
4911 }
4912
4913
4914 /**********************************************************************
4915  *              InsertMenuItemA    (USER32.@)
4916  */
4917 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4918                                 const MENUITEMINFOA *lpmii)
4919 {
4920     MENUITEM *item;
4921     MENUITEMINFOW mii;
4922
4923     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4924
4925     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4926
4927     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4928     return SetMenuItemInfo_common(item, &mii, FALSE);
4929 }
4930
4931
4932 /**********************************************************************
4933  *              InsertMenuItemW    (USER32.@)
4934  */
4935 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4936                                 const MENUITEMINFOW *lpmii)
4937 {
4938     MENUITEM *item;
4939     MENUITEMINFOW mii;
4940
4941     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4942
4943     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4944
4945     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4946     return SetMenuItemInfo_common(item, &mii, TRUE);
4947 }
4948
4949 /**********************************************************************
4950  *              CheckMenuRadioItem    (USER32.@)
4951  */
4952
4953 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4954                                    UINT first, UINT last, UINT check,
4955                                    UINT bypos)
4956 {
4957     BOOL done = FALSE;
4958     UINT i;
4959     MENUITEM *mi_first = NULL, *mi_check;
4960     HMENU m_first, m_check;
4961
4962     for (i = first; i <= last; i++)
4963     {
4964         UINT pos = i;
4965
4966         if (!mi_first)
4967         {
4968             m_first = hMenu;
4969             mi_first = MENU_FindItem(&m_first, &pos, bypos);
4970             if (!mi_first) continue;
4971             mi_check = mi_first;
4972             m_check = m_first;
4973         }
4974         else
4975         {
4976             m_check = hMenu;
4977             mi_check = MENU_FindItem(&m_check, &pos, bypos);
4978             if (!mi_check) continue;
4979         }
4980
4981         if (m_first != m_check) continue;
4982         if (mi_check->fType == MFT_SEPARATOR) continue;
4983
4984         if (i == check)
4985         {
4986             mi_check->fType |= MFT_RADIOCHECK;
4987             mi_check->fState |= MFS_CHECKED;
4988             done = TRUE;
4989         }
4990         else
4991         {
4992             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4993             mi_check->fState &= ~MFS_CHECKED;
4994         }
4995     }
4996
4997     return done;
4998 }
4999
5000
5001 /**********************************************************************
5002  *              GetMenuItemRect    (USER32.@)
5003  *
5004  *      ATTENTION: Here, the returned values in rect are the screen
5005  *                 coordinates of the item just like if the menu was
5006  *                 always on the upper left side of the application.
5007  *
5008  */
5009 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5010                                  LPRECT rect)
5011 {
5012      POPUPMENU *itemMenu;
5013      MENUITEM *item;
5014      HWND referenceHwnd;
5015
5016      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5017
5018      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5019      referenceHwnd = hwnd;
5020
5021      if(!hwnd)
5022      {
5023          itemMenu = MENU_GetMenu(hMenu);
5024          if (itemMenu == NULL)
5025              return FALSE;
5026
5027          if(itemMenu->hWnd == 0)
5028              return FALSE;
5029          referenceHwnd = itemMenu->hWnd;
5030      }
5031
5032      if ((rect == NULL) || (item == NULL))
5033          return FALSE;
5034
5035      *rect = item->rect;
5036
5037      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5038
5039      return TRUE;
5040 }
5041
5042 /**********************************************************************
5043  *              SetMenuInfo    (USER32.@)
5044  *
5045  * FIXME
5046  *      actually use the items to draw the menu
5047  *      (recalculate and/or redraw)
5048  */
5049 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5050 {
5051     POPUPMENU *menu;
5052     if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5053
5054     if (lpmi->fMask & MIM_BACKGROUND)
5055         menu->hbrBack = lpmi->hbrBack;
5056
5057     if (lpmi->fMask & MIM_HELPID)
5058         menu->dwContextHelpID = lpmi->dwContextHelpID;
5059
5060     if (lpmi->fMask & MIM_MAXHEIGHT)
5061         menu->cyMax = lpmi->cyMax;
5062
5063     if (lpmi->fMask & MIM_MENUDATA)
5064         menu->dwMenuData = lpmi->dwMenuData;
5065
5066     if (lpmi->fMask & MIM_STYLE)
5067         menu->dwStyle = lpmi->dwStyle;
5068
5069     if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5070         int i;
5071         MENUITEM *item = menu->items;
5072         for( i = menu->nItems; i; i--, item++)
5073             if( item->fType & MF_POPUP)
5074                 menu_SetMenuInfo( item->hSubMenu, lpmi);
5075     }
5076     return TRUE;
5077 }
5078
5079 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5080 {
5081     TRACE("(%p %p)\n", hMenu, lpmi);
5082     if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5083         if( lpmi->fMask & MIM_STYLE) {
5084             if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5085             if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5086             if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5087         }
5088         return TRUE;
5089     }
5090     SetLastError( ERROR_INVALID_PARAMETER);
5091     return FALSE;
5092 }
5093
5094 /**********************************************************************
5095  *              GetMenuInfo    (USER32.@)
5096  *
5097  *  NOTES
5098  *      win98/NT5.0
5099  *
5100  */
5101 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5102 {   POPUPMENU *menu;
5103
5104     TRACE("(%p %p)\n", hMenu, lpmi);
5105
5106     if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5107     {
5108
5109         if (lpmi->fMask & MIM_BACKGROUND)
5110             lpmi->hbrBack = menu->hbrBack;
5111
5112         if (lpmi->fMask & MIM_HELPID)
5113             lpmi->dwContextHelpID = menu->dwContextHelpID;
5114
5115         if (lpmi->fMask & MIM_MAXHEIGHT)
5116             lpmi->cyMax = menu->cyMax;
5117
5118         if (lpmi->fMask & MIM_MENUDATA)
5119             lpmi->dwMenuData = menu->dwMenuData;
5120
5121         if (lpmi->fMask & MIM_STYLE)
5122             lpmi->dwStyle = menu->dwStyle;
5123
5124         return TRUE;
5125     }
5126     SetLastError( ERROR_INVALID_PARAMETER);
5127     return FALSE;
5128 }
5129
5130
5131 /**********************************************************************
5132  *         SetMenuContextHelpId    (USER32.@)
5133  */
5134 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5135 {
5136     LPPOPUPMENU menu;
5137
5138     TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5139
5140     if ((menu = MENU_GetMenu(hMenu)))
5141     {
5142         menu->dwContextHelpID = dwContextHelpID;
5143         return TRUE;
5144     }
5145     return FALSE;
5146 }
5147
5148
5149 /**********************************************************************
5150  *         GetMenuContextHelpId    (USER32.@)
5151  */
5152 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5153 {
5154     LPPOPUPMENU menu;
5155
5156     TRACE("(%p)\n", hMenu);
5157
5158     if ((menu = MENU_GetMenu(hMenu)))
5159     {
5160         return menu->dwContextHelpID;
5161     }
5162     return 0;
5163 }
5164
5165 /**********************************************************************
5166  *         MenuItemFromPoint    (USER32.@)
5167  */
5168 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5169 {
5170     POPUPMENU *menu = MENU_GetMenu(hMenu);
5171     UINT pos;
5172
5173     /*FIXME: Do we have to handle hWnd here? */
5174     if (!menu) return -1;
5175     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5176     return pos;
5177 }
5178
5179
5180 /**********************************************************************
5181  *           translate_accelerator
5182  */
5183 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5184                                    BYTE fVirt, WORD key, WORD cmd )
5185 {
5186     INT mask = 0;
5187     UINT mesg = 0;
5188
5189     if (wParam != key) return FALSE;
5190
5191     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5192     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5193     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5194
5195     if (message == WM_CHAR || message == WM_SYSCHAR)
5196     {
5197         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5198         {
5199             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5200             goto found;
5201         }
5202     }
5203     else
5204     {
5205         if(fVirt & FVIRTKEY)
5206         {
5207             TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5208                           wParam, 0xff & HIWORD(lParam));
5209
5210             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5211             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5212         }
5213         else
5214         {
5215             if (!(lParam & 0x01000000))  /* no special_key */
5216             {
5217                 if ((fVirt & FALT) && (lParam & 0x20000000))
5218                 {                              /* ^^ ALT pressed */
5219                     TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5220                     goto found;
5221                 }
5222             }
5223         }
5224     }
5225     return FALSE;
5226
5227  found:
5228     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5229         mesg = 1;
5230     else
5231     {
5232         HMENU hMenu, hSubMenu, hSysMenu;
5233         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5234
5235         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5236         hSysMenu = get_win_sys_menu( hWnd );
5237
5238         /* find menu item and ask application to initialize it */
5239         /* 1. in the system menu */
5240         hSubMenu = hSysMenu;
5241         nPos = cmd;
5242         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5243         {
5244             if (GetCapture())
5245                 mesg = 2;
5246             if (!IsWindowEnabled(hWnd))
5247                 mesg = 3;
5248             else
5249             {
5250                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5251                 if(hSubMenu != hSysMenu)
5252                 {
5253                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5254                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5255                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5256                 }
5257                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5258             }
5259         }
5260         else /* 2. in the window's menu */
5261         {
5262             hSubMenu = hMenu;
5263             nPos = cmd;
5264             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5265             {
5266                 if (GetCapture())
5267                     mesg = 2;
5268                 if (!IsWindowEnabled(hWnd))
5269                     mesg = 3;
5270                 else
5271                 {
5272                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5273                     if(hSubMenu != hMenu)
5274                     {
5275                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5276                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5277                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5278                     }
5279                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5280                 }
5281             }
5282         }
5283
5284         if (mesg == 0)
5285         {
5286             if (uSysStat != (UINT)-1)
5287             {
5288                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5289                     mesg=4;
5290                 else
5291                     mesg=WM_SYSCOMMAND;
5292             }
5293             else
5294             {
5295                 if (uStat != (UINT)-1)
5296                 {
5297                     if (IsIconic(hWnd))
5298                         mesg=5;
5299                     else
5300                     {
5301                         if (uStat & (MF_DISABLED|MF_GRAYED))
5302                             mesg=6;
5303                         else
5304                             mesg=WM_COMMAND;
5305                     }
5306                 }
5307                 else
5308                     mesg=WM_COMMAND;
5309             }
5310         }
5311     }
5312
5313     if( mesg==WM_COMMAND )
5314     {
5315         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5316         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5317     }
5318     else if( mesg==WM_SYSCOMMAND )
5319     {
5320         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5321         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5322     }
5323     else
5324     {
5325         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5326          *   #0: unknown (please report!)
5327          *   #1: for WM_KEYUP,WM_SYSKEYUP
5328          *   #2: mouse is captured
5329          *   #3: window is disabled
5330          *   #4: it's a disabled system menu option
5331          *   #5: it's a menu option, but window is iconic
5332          *   #6: it's a menu option, but disabled
5333          */
5334         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5335         if(mesg==0)
5336             ERR_(accel)(" unknown reason - please report!\n");
5337     }
5338     return TRUE;
5339 }
5340
5341 /**********************************************************************
5342  *      TranslateAcceleratorA     (USER32.@)
5343  *      TranslateAccelerator      (USER32.@)
5344  */
5345 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5346 {
5347     /* YES, Accel16! */
5348     LPACCEL16 lpAccelTbl;
5349     int i;
5350     WPARAM wParam;
5351
5352     if (!hWnd || !msg) return 0;
5353
5354     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5355     {
5356         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5357         return 0;
5358     }
5359
5360     wParam = msg->wParam;
5361
5362     switch (msg->message)
5363     {
5364     case WM_KEYDOWN:
5365     case WM_SYSKEYDOWN:
5366         break;
5367
5368     case WM_CHAR:
5369     case WM_SYSCHAR:
5370         {
5371             char ch = LOWORD(wParam);
5372             WCHAR wch;
5373             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5374             wParam = MAKEWPARAM(wch, HIWORD(wParam));
5375         }
5376         break;
5377
5378     default:
5379         return 0;
5380     }
5381
5382     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5383                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5384     i = 0;
5385     do
5386     {
5387         if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5388                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5389             return 1;
5390     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5391
5392     return 0;
5393 }
5394
5395 /**********************************************************************
5396  *      TranslateAcceleratorW     (USER32.@)
5397  */
5398 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5399 {
5400     /* YES, Accel16! */
5401     LPACCEL16 lpAccelTbl;
5402     int i;
5403
5404     if (!hWnd || !msg) return 0;
5405
5406     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5407     {
5408         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5409         return 0;
5410     }
5411
5412     switch (msg->message)
5413     {
5414     case WM_KEYDOWN:
5415     case WM_SYSKEYDOWN:
5416     case WM_CHAR:
5417     case WM_SYSCHAR:
5418         break;
5419
5420     default:
5421         return 0;
5422     }
5423
5424     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5425                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5426     i = 0;
5427     do
5428     {
5429         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5430                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5431             return 1;
5432     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5433
5434     return 0;
5435 }