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