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