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