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