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