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