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