Assorted typo, spelling, wording and case fixes.
[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 through 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 flags %x\n", pos, flags);
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     /* use default alignment for submenus */
2399     wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2400
2401     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2402                     rect.left, rect.top, rect.right, rect.bottom );
2403     if (selectFirst)
2404         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2405     return item->hSubMenu;
2406 }
2407
2408
2409
2410 /**********************************************************************
2411  *         MENU_IsMenuActive
2412  */
2413 HWND MENU_IsMenuActive(void)
2414 {
2415     return top_popup;
2416 }
2417
2418 /**********************************************************************
2419  *         MENU_EndMenu
2420  *
2421  * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2422  *
2423  * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2424  */
2425 void MENU_EndMenu( HWND hwnd )
2426 {
2427     POPUPMENU *menu;
2428     menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2429     if (menu && hwnd == menu->hwndOwner) EndMenu();
2430 }
2431
2432 /***********************************************************************
2433  *           MENU_PtMenu
2434  *
2435  * Walks menu chain trying to find a menu pt maps to.
2436  */
2437 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2438 {
2439    POPUPMENU *menu = MENU_GetMenu( hMenu );
2440    UINT item = menu->FocusedItem;
2441    HMENU ret;
2442
2443    /* try subpopup first (if any) */
2444    ret = (item != NO_SELECTED_ITEM &&
2445           (menu->items[item].fType & MF_POPUP) &&
2446           (menu->items[item].fState & MF_MOUSESELECT))
2447         ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2448
2449    if (!ret)  /* check the current window (avoiding WM_HITTEST) */
2450    {
2451        INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2452        if( menu->wFlags & MF_POPUP )
2453        {
2454            if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2455        }
2456        else if (ht == HTSYSMENU)
2457            ret = get_win_sys_menu( menu->hWnd );
2458        else if (ht == HTMENU)
2459            ret = GetMenu( menu->hWnd );
2460    }
2461    return ret;
2462 }
2463
2464 /***********************************************************************
2465  *           MENU_ExecFocusedItem
2466  *
2467  * Execute a menu item (for instance when user pressed Enter).
2468  * Return the wID of the executed item. Otherwise, -1 indicating
2469  * that no menu item was executed, -2 if a popup is shown;
2470  * Have to receive the flags for the TrackPopupMenu options to avoid
2471  * sending unwanted message.
2472  *
2473  */
2474 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2475 {
2476     MENUITEM *item;
2477     POPUPMENU *menu = MENU_GetMenu( hMenu );
2478
2479     TRACE("%p hmenu=%p\n", pmt, hMenu);
2480
2481     if (!menu || !menu->nItems ||
2482         (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2483
2484     item = &menu->items[menu->FocusedItem];
2485
2486     TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2487
2488     if (!(item->fType & MF_POPUP))
2489     {
2490         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2491         {
2492             /* If TPM_RETURNCMD is set you return the id, but
2493                do not send a message to the owner */
2494             if(!(wFlags & TPM_RETURNCMD))
2495             {
2496                 if( menu->wFlags & MF_SYSMENU )
2497                     PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2498                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2499                 else
2500                 {
2501                     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2502                     DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2503
2504                     if (dwStyle & MNS_NOTIFYBYPOS)
2505                         PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2506                                       (LPARAM)hMenu);
2507                     else
2508                         PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2509                 }
2510             }
2511             return item->wID;
2512         }
2513     }
2514     else
2515     {
2516         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2517         return -2;
2518     }
2519
2520     return -1;
2521 }
2522
2523 /***********************************************************************
2524  *           MENU_SwitchTracking
2525  *
2526  * Helper function for menu navigation routines.
2527  */
2528 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2529 {
2530     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2531     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2532
2533     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2534
2535     if( pmt->hTopMenu != hPtMenu &&
2536         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2537     {
2538         /* both are top level menus (system and menu-bar) */
2539         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2540         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2541         pmt->hTopMenu = hPtMenu;
2542     }
2543     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2544     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2545 }
2546
2547
2548 /***********************************************************************
2549  *           MENU_ButtonDown
2550  *
2551  * Return TRUE if we can go on with menu tracking.
2552  */
2553 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2554 {
2555     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2556
2557     if (hPtMenu)
2558     {
2559         UINT id = 0;
2560         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2561         MENUITEM *item;
2562
2563         if( IS_SYSTEM_MENU(ptmenu) )
2564             item = ptmenu->items;
2565         else
2566             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2567
2568         if( item )
2569         {
2570             if( ptmenu->FocusedItem != id )
2571                 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2572
2573             /* If the popup menu is not already "popped" */
2574             if(!(item->fState & MF_MOUSESELECT ))
2575             {
2576                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2577             }
2578
2579             return TRUE;
2580         }
2581         /* Else the click was on the menu bar, finish the tracking */
2582     }
2583     return FALSE;
2584 }
2585
2586 /***********************************************************************
2587  *           MENU_ButtonUp
2588  *
2589  * Return the value of MENU_ExecFocusedItem if
2590  * the selected item was not a popup. Else open the popup.
2591  * A -1 return value indicates that we go on with menu tracking.
2592  *
2593  */
2594 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2595 {
2596     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2597
2598     if (hPtMenu)
2599     {
2600         UINT id = 0;
2601         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2602         MENUITEM *item;
2603
2604         if( IS_SYSTEM_MENU(ptmenu) )
2605             item = ptmenu->items;
2606         else
2607             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2608
2609         if( item && (ptmenu->FocusedItem == id ))
2610         {
2611             debug_print_menuitem ("FocusedItem: ", item, "");
2612
2613             if( !(item->fType & MF_POPUP) )
2614             {
2615                 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2616                 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2617                 return executedMenuId;
2618             }
2619
2620             /* If we are dealing with the top-level menu            */
2621             /* and this is a click on an already "popped" item:     */
2622             /* Stop the menu tracking and close the opened submenus */
2623             if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2624                 return 0;
2625         }
2626         ptmenu->bTimeToHide = TRUE;
2627     }
2628     return -1;
2629 }
2630
2631
2632 /***********************************************************************
2633  *           MENU_MouseMove
2634  *
2635  * Return TRUE if we can go on with menu tracking.
2636  */
2637 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2638 {
2639     UINT id = NO_SELECTED_ITEM;
2640     POPUPMENU *ptmenu = NULL;
2641
2642     if( hPtMenu )
2643     {
2644         ptmenu = MENU_GetMenu( hPtMenu );
2645         if( IS_SYSTEM_MENU(ptmenu) )
2646             id = 0;
2647         else
2648             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2649     }
2650
2651     if( id == NO_SELECTED_ITEM )
2652     {
2653         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2654                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2655
2656     }
2657     else if( ptmenu->FocusedItem != id )
2658     {
2659             MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2660             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2661     }
2662     return TRUE;
2663 }
2664
2665
2666 /***********************************************************************
2667  *           MENU_DoNextMenu
2668  *
2669  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2670  */
2671 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2672 {
2673     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2674     BOOL atEnd = FALSE;
2675
2676     /* When skipping left, we need to do something special after the
2677        first menu.                                                  */
2678     if (vk == VK_LEFT && menu->FocusedItem == 0)
2679     {
2680         atEnd = TRUE;
2681     }
2682     /* When skipping right, for the non-system menu, we need to
2683        handle the last non-special menu item (ie skip any window
2684        icons such as MDI maximize, restore or close)             */
2685     else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2686     {
2687         UINT i = menu->FocusedItem + 1;
2688         while (i < menu->nItems) {
2689             if ((menu->items[i].wID >= SC_SIZE &&
2690                  menu->items[i].wID <= SC_RESTORE)) {
2691                 i++;
2692             } else break;
2693         }
2694         if (i == menu->nItems) {
2695             atEnd = TRUE;
2696         }
2697     }
2698     /* When skipping right, we need to cater for the system menu */
2699     else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2700     {
2701         if (menu->FocusedItem == (menu->nItems - 1)) {
2702             atEnd = TRUE;
2703         }
2704     }
2705
2706     if( atEnd )
2707     {
2708         MDINEXTMENU next_menu;
2709         HMENU hNewMenu;
2710         HWND  hNewWnd;
2711         UINT  id = 0;
2712
2713         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2714         next_menu.hmenuNext = 0;
2715         next_menu.hwndNext = 0;
2716         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2717
2718         TRACE("%p [%p] -> %p [%p]\n",
2719               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2720
2721         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2722         {
2723             DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2724             hNewWnd = pmt->hOwnerWnd;
2725             if( IS_SYSTEM_MENU(menu) )
2726             {
2727                 /* switch to the menu bar */
2728
2729                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2730
2731                 if( vk == VK_LEFT )
2732                 {
2733                     menu = MENU_GetMenu( hNewMenu );
2734                     id = menu->nItems - 1;
2735
2736                     /* Skip backwards over any system predefined icons,
2737                        eg. MDI close, restore etc icons                 */
2738                     while ((id > 0) &&
2739                            (menu->items[id].wID >= SC_SIZE &&
2740                             menu->items[id].wID <= SC_RESTORE)) id--;
2741                 }
2742             }
2743             else if (style & WS_SYSMENU )
2744             {
2745                 /* switch to the system menu */
2746                 hNewMenu = get_win_sys_menu( hNewWnd );
2747             }
2748             else return FALSE;
2749         }
2750         else    /* application returned a new menu to switch to */
2751         {
2752             hNewMenu = next_menu.hmenuNext;
2753             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2754
2755             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2756             {
2757                 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2758
2759                 if (style & WS_SYSMENU &&
2760                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2761                 {
2762                     /* get the real system menu */
2763                     hNewMenu =  get_win_sys_menu(hNewWnd);
2764                 }
2765                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2766                 {
2767                     /* FIXME: Not sure what to do here;
2768                      * perhaps try to track hNewMenu as a popup? */
2769
2770                     TRACE(" -- got confused.\n");
2771                     return FALSE;
2772                 }
2773             }
2774             else return FALSE;
2775         }
2776
2777         if( hNewMenu != pmt->hTopMenu )
2778         {
2779             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2780                     FALSE, 0 );
2781             if( pmt->hCurrentMenu != pmt->hTopMenu )
2782                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2783         }
2784
2785         if( hNewWnd != pmt->hOwnerWnd )
2786         {
2787             pmt->hOwnerWnd = hNewWnd;
2788             set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2789         }
2790
2791         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2792         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2793
2794         return TRUE;
2795     }
2796     return FALSE;
2797 }
2798
2799 /***********************************************************************
2800  *           MENU_SuspendPopup
2801  *
2802  * The idea is not to show the popup if the next input message is
2803  * going to hide it anyway.
2804  */
2805 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2806 {
2807     MSG msg;
2808
2809     msg.hwnd = pmt->hOwnerWnd;
2810
2811     PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2812     pmt->trackFlags |= TF_SKIPREMOVE;
2813
2814     switch( uMsg )
2815     {
2816         case WM_KEYDOWN:
2817              PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2818              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2819              {
2820                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2821                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2822                  if( msg.message == WM_KEYDOWN &&
2823                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2824                  {
2825                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2826                      return TRUE;
2827                  }
2828              }
2829              break;
2830     }
2831
2832     /* failures go through this */
2833     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2834     return FALSE;
2835 }
2836
2837 /***********************************************************************
2838  *           MENU_KeyEscape
2839  *
2840  * Handle a VK_ESCAPE key event in a menu.
2841  */
2842 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2843 {
2844     BOOL bEndMenu = TRUE;
2845
2846     if (pmt->hCurrentMenu != pmt->hTopMenu)
2847     {
2848         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2849
2850         if (menu->wFlags & MF_POPUP)
2851         {
2852             HMENU hmenutmp, hmenuprev;
2853
2854             hmenuprev = hmenutmp = pmt->hTopMenu;
2855
2856             /* close topmost popup */
2857             while (hmenutmp != pmt->hCurrentMenu)
2858             {
2859                 hmenuprev = hmenutmp;
2860                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2861             }
2862
2863             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2864             pmt->hCurrentMenu = hmenuprev;
2865             bEndMenu = FALSE;
2866         }
2867     }
2868
2869     return bEndMenu;
2870 }
2871
2872 /***********************************************************************
2873  *           MENU_KeyLeft
2874  *
2875  * Handle a VK_LEFT key event in a menu.
2876  */
2877 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2878 {
2879     POPUPMENU *menu;
2880     HMENU hmenutmp, hmenuprev;
2881     UINT  prevcol;
2882
2883     hmenuprev = hmenutmp = pmt->hTopMenu;
2884     menu = MENU_GetMenu( hmenutmp );
2885
2886     /* Try to move 1 column left (if possible) */
2887     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2888         NO_SELECTED_ITEM ) {
2889
2890         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2891                          prevcol, TRUE, 0 );
2892         return;
2893     }
2894
2895     /* close topmost popup */
2896     while (hmenutmp != pmt->hCurrentMenu)
2897     {
2898         hmenuprev = hmenutmp;
2899         hmenutmp = MENU_GetSubPopup( hmenuprev );
2900     }
2901
2902     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2903     pmt->hCurrentMenu = hmenuprev;
2904
2905     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2906     {
2907         /* move menu bar selection if no more popups are left */
2908
2909         if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2910              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2911
2912         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2913         {
2914            /* A sublevel menu was displayed - display the next one
2915             * unless there is another displacement coming up */
2916
2917             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2918                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2919                                                 pmt->hTopMenu, TRUE, wFlags);
2920         }
2921     }
2922 }
2923
2924
2925 /***********************************************************************
2926  *           MENU_KeyRight
2927  *
2928  * Handle a VK_RIGHT key event in a menu.
2929  */
2930 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2931 {
2932     HMENU hmenutmp;
2933     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2934     UINT  nextcol;
2935
2936     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2937           pmt->hCurrentMenu,
2938           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2939           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2940
2941     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2942     {
2943         /* If already displaying a popup, try to display sub-popup */
2944
2945         hmenutmp = pmt->hCurrentMenu;
2946         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2947
2948         /* if subpopup was displayed then we are done */
2949         if (hmenutmp != pmt->hCurrentMenu) return;
2950     }
2951
2952     /* Check to see if there's another column */
2953     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2954         NO_SELECTED_ITEM ) {
2955         TRACE("Going to %d.\n", nextcol );
2956         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2957                          nextcol, TRUE, 0 );
2958         return;
2959     }
2960
2961     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2962     {
2963         if( pmt->hCurrentMenu != pmt->hTopMenu )
2964         {
2965             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2966             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2967         } else hmenutmp = 0;
2968
2969         /* try to move to the next item */
2970         if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2971              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2972
2973         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2974             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2975                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2976                                                        pmt->hTopMenu, TRUE, wFlags);
2977     }
2978 }
2979
2980 static void CALLBACK release_capture( BOOL __normal )
2981 {
2982     set_capture_window( 0, GUI_INMENUMODE, NULL );
2983 }
2984
2985 /***********************************************************************
2986  *           MENU_TrackMenu
2987  *
2988  * Menu tracking code.
2989  */
2990 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2991                             HWND hwnd, const RECT *lprect )
2992 {
2993     MSG msg;
2994     POPUPMENU *menu;
2995     BOOL fRemove;
2996     INT executedMenuId = -1;
2997     MTRACKER mt;
2998     BOOL enterIdleSent = FALSE;
2999     HWND capture_win;
3000
3001     mt.trackFlags = 0;
3002     mt.hCurrentMenu = hmenu;
3003     mt.hTopMenu = hmenu;
3004     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3005     mt.pt.x = x;
3006     mt.pt.y = y;
3007
3008     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3009           hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3010
3011     fEndMenu = FALSE;
3012     if (!(menu = MENU_GetMenu( hmenu )))
3013     {
3014         WARN("Invalid menu handle %p\n", hmenu);
3015         SetLastError(ERROR_INVALID_MENU_HANDLE);
3016         return FALSE;
3017     }
3018
3019     if (wFlags & TPM_BUTTONDOWN)
3020     {
3021         /* Get the result in order to start the tracking or not */
3022         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3023         fEndMenu = !fRemove;
3024     }
3025
3026     if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3027
3028     /* owner may not be visible when tracking a popup, so use the menu itself */
3029     capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3030     set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3031
3032     __TRY while (!fEndMenu)
3033     {
3034         menu = MENU_GetMenu( mt.hCurrentMenu );
3035         if (!menu) /* sometimes happens if I do a window manager close */
3036             break;
3037
3038         /* we have to keep the message in the queue until it's
3039          * clear that menu loop is not over yet. */
3040
3041         for (;;)
3042         {
3043             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3044             {
3045                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3046                 /* remove the message from the queue */
3047                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3048             }
3049             else
3050             {
3051                 if (!enterIdleSent)
3052                 {
3053                     HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3054                     enterIdleSent = TRUE;
3055                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3056                 }
3057                 WaitMessage();
3058             }
3059         }
3060
3061         /* check if EndMenu() tried to cancel us, by posting this message */
3062         if(msg.message == WM_CANCELMODE)
3063         {
3064             /* we are now out of the loop */
3065             fEndMenu = TRUE;
3066
3067             /* remove the message from the queue */
3068             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3069
3070             /* break out of internal loop, ala ESCAPE */
3071             break;
3072         }
3073
3074         TranslateMessage( &msg );
3075         mt.pt = msg.pt;
3076
3077         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3078           enterIdleSent=FALSE;
3079
3080         fRemove = FALSE;
3081         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3082         {
3083             /*
3084              * Use the mouse coordinates in lParam instead of those in the MSG
3085              * struct to properly handle synthetic messages. They are already
3086              * in screen coordinates.
3087              */
3088             mt.pt.x = (short)LOWORD(msg.lParam);
3089             mt.pt.y = (short)HIWORD(msg.lParam);
3090
3091             /* Find a menu for this mouse event */
3092             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3093
3094             switch(msg.message)
3095             {
3096                 /* no WM_NC... messages in captured state */
3097
3098                 case WM_RBUTTONDBLCLK:
3099                 case WM_RBUTTONDOWN:
3100                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3101                     /* fall through */
3102                 case WM_LBUTTONDBLCLK:
3103                 case WM_LBUTTONDOWN:
3104                     /* If the message belongs to the menu, removes it from the queue */
3105                     /* Else, end menu tracking */
3106                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3107                     fEndMenu = !fRemove;
3108                     break;
3109
3110                 case WM_RBUTTONUP:
3111                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3112                     /* fall through */
3113                 case WM_LBUTTONUP:
3114                     /* Check if a menu was selected by the mouse */
3115                     if (hmenu)
3116                     {
3117                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3118                         TRACE("executedMenuId %d\n", executedMenuId);
3119
3120                         /* End the loop if executedMenuId is an item ID */
3121                         /* or if the job was done (executedMenuId = 0). */
3122                         fEndMenu = fRemove = (executedMenuId != -1);
3123                     }
3124                     /* No menu was selected by the mouse */
3125                     /* if the function was called by TrackPopupMenu, continue
3126                        with the menu tracking. If not, stop it */
3127                     else
3128                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3129
3130                     break;
3131
3132                 case WM_MOUSEMOVE:
3133                     /* the selected menu item must be changed every time */
3134                      /* the mouse moves. */
3135
3136                     if (hmenu)
3137                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3138
3139             } /* switch(msg.message) - mouse */
3140         }
3141         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3142         {
3143             fRemove = TRUE;  /* Keyboard messages are always removed */
3144             switch(msg.message)
3145             {
3146             case WM_KEYDOWN:
3147             case WM_SYSKEYDOWN:
3148                 switch(msg.wParam)
3149                 {
3150                 case VK_MENU:
3151                 case VK_F10:
3152                     fEndMenu = TRUE;
3153                     break;
3154
3155                 case VK_HOME:
3156                 case VK_END:
3157                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3158                                      NO_SELECTED_ITEM, FALSE, 0 );
3159                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3160                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3161                     break;
3162
3163                 case VK_UP:
3164                 case VK_DOWN: /* If on menu bar, pull-down the menu */
3165
3166                     menu = MENU_GetMenu( mt.hCurrentMenu );
3167                     if (!(menu->wFlags & MF_POPUP))
3168                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3169                     else      /* otherwise try to move selection */
3170                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
3171                                        (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3172                     break;
3173
3174                 case VK_LEFT:
3175                     MENU_KeyLeft( &mt, wFlags );
3176                     break;
3177
3178                 case VK_RIGHT:
3179                     MENU_KeyRight( &mt, wFlags );
3180                     break;
3181
3182                 case VK_ESCAPE:
3183                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
3184                     break;
3185
3186                 case VK_F1:
3187                     {
3188                         HELPINFO hi;
3189                         hi.cbSize = sizeof(HELPINFO);
3190                         hi.iContextType = HELPINFO_MENUITEM;
3191                         if (menu->FocusedItem == NO_SELECTED_ITEM)
3192                             hi.iCtrlId = 0;
3193                         else
3194                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3195                         hi.hItemHandle = hmenu;
3196                         hi.dwContextId = menu->dwContextHelpID;
3197                         hi.MousePos = msg.pt;
3198                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3199                         break;
3200                     }
3201
3202                 default:
3203                     break;
3204                 }
3205                 break;  /* WM_KEYDOWN */
3206
3207             case WM_CHAR:
3208             case WM_SYSCHAR:
3209                 {
3210                     UINT        pos;
3211
3212                     if (msg.wParam == '\r' || msg.wParam == ' ')
3213                     {
3214                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3215                         fEndMenu = (executedMenuId != -2);
3216
3217                         break;
3218                     }
3219
3220                       /* Hack to avoid control chars. */
3221                       /* We will find a better way real soon... */
3222                     if (msg.wParam < 32) break;
3223
3224                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3225                                               LOWORD(msg.wParam), FALSE );
3226                     if (pos == (UINT)-2) fEndMenu = TRUE;
3227                     else if (pos == (UINT)-1) MessageBeep(0);
3228                     else
3229                     {
3230                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3231                                 TRUE, 0 );
3232                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3233                         fEndMenu = (executedMenuId != -2);
3234                     }
3235                 }
3236                 break;
3237             }  /* switch(msg.message) - kbd */
3238         }
3239         else
3240         {
3241             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3242             DispatchMessageW( &msg );
3243             continue;
3244         }
3245
3246         if (!fEndMenu) fRemove = TRUE;
3247
3248         /* finally remove message from the queue */
3249
3250         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3251             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3252         else mt.trackFlags &= ~TF_SKIPREMOVE;
3253     }
3254     __FINALLY( release_capture )
3255
3256     /* If dropdown is still painted and the close box is clicked on
3257        then the menu will be destroyed as part of the DispatchMessage above.
3258        This will then invalidate the menu handle in mt.hTopMenu. We should
3259        check for this first.  */
3260     if( IsMenu( mt.hTopMenu ) )
3261     {
3262         menu = MENU_GetMenu( mt.hTopMenu );
3263
3264         if( IsWindow( mt.hOwnerWnd ) )
3265         {
3266             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3267
3268             if (menu && (menu->wFlags & MF_POPUP))
3269             {
3270                 DestroyWindow( menu->hWnd );
3271                 menu->hWnd = 0;
3272
3273                 if (!(wFlags & TPM_NONOTIFY))
3274                    SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3275                                  MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3276             }
3277             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3278             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3279         }
3280
3281         /* Reset the variable for hiding menu */
3282         if( menu ) menu->bTimeToHide = FALSE;
3283     }
3284
3285     /* The return value is only used by TrackPopupMenu */
3286     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3287     if (executedMenuId == -1) executedMenuId = 0;
3288     return executedMenuId;
3289 }
3290
3291 /***********************************************************************
3292  *           MENU_InitTracking
3293  */
3294 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3295 {
3296     POPUPMENU *menu;
3297     
3298     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3299
3300     HideCaret(0);
3301
3302     /* This makes the menus of applications built with Delphi work.
3303      * It also enables menus to be displayed in more than one window,
3304      * but there are some bugs left that need to be fixed in this case.
3305      */
3306     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3307
3308     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3309     if (!(wFlags & TPM_NONOTIFY))
3310        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3311
3312     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3313
3314     if (!(wFlags & TPM_NONOTIFY))
3315     {
3316        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3317        /* If an app changed/recreated menu bar entries in WM_INITMENU
3318         * menu sizes will be recalculated once the menu created/shown.
3319         */
3320     }
3321
3322     return TRUE;
3323 }
3324
3325 /***********************************************************************
3326  *           MENU_ExitTracking
3327  */
3328 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3329 {
3330     TRACE("hwnd=%p\n", hWnd);
3331
3332     SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3333     ShowCaret(0);
3334     top_popup = 0;
3335     top_popup_hmenu = NULL;
3336     return TRUE;
3337 }
3338
3339 /***********************************************************************
3340  *           MENU_TrackMouseMenuBar
3341  *
3342  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3343  */
3344 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3345 {
3346     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3347     UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3348
3349     TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3350
3351     if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3352     if (IsMenu(hMenu))
3353     {
3354         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3355         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3356         MENU_ExitTracking(hWnd, FALSE);
3357     }
3358 }
3359
3360
3361 /***********************************************************************
3362  *           MENU_TrackKbdMenuBar
3363  *
3364  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3365  */
3366 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3367 {
3368     UINT uItem = NO_SELECTED_ITEM;
3369     HMENU hTrackMenu;
3370     UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3371
3372     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3373
3374     /* find window that has a menu */
3375
3376     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3377         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3378
3379     /* check if we have to track a system menu */
3380
3381     hTrackMenu = GetMenu( hwnd );
3382     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3383     {
3384         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3385         hTrackMenu = get_win_sys_menu( hwnd );
3386         uItem = 0;
3387         wParam |= HTSYSMENU; /* prevent item lookup */
3388     }
3389     if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3390
3391     if (!IsMenu( hTrackMenu )) return;
3392
3393     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3394
3395     if( wChar && wChar != ' ' )
3396     {
3397         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3398         if ( uItem >= (UINT)(-2) )
3399         {
3400             if( uItem == (UINT)(-1) ) MessageBeep(0);
3401             /* schedule end of menu tracking */
3402             wFlags |= TF_ENDMENU;
3403             goto track_menu;
3404         }
3405     }
3406
3407     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3408
3409     if (!(wParam & HTSYSMENU) || wChar == ' ')
3410     {
3411         if( uItem == NO_SELECTED_ITEM )
3412             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3413         else
3414             PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3415     }
3416
3417 track_menu:
3418     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3419     MENU_ExitTracking( hwnd, FALSE );
3420 }
3421
3422 /**********************************************************************
3423  *           TrackPopupMenuEx   (USER32.@)
3424  */
3425 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3426                               HWND hWnd, LPTPMPARAMS lpTpm )
3427 {
3428     POPUPMENU *menu;
3429     BOOL ret = FALSE;
3430
3431     TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3432             hMenu, wFlags, x, y, hWnd, lpTpm,
3433             lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3434
3435     /* Parameter check */
3436     /* FIXME: this check is performed several times, here and in the called
3437        functions. That could be optimized */
3438     if (!(menu = MENU_GetMenu( hMenu )))
3439     {
3440         SetLastError( ERROR_INVALID_MENU_HANDLE );
3441         return FALSE;
3442     }
3443
3444     if (IsWindow(menu->hWnd))
3445     {
3446         SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3447         return FALSE;
3448     }
3449
3450     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3451
3452     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3453     if (!(wFlags & TPM_NONOTIFY))
3454         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3455
3456     if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3457         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3458                               lpTpm ? &lpTpm->rcExclude : NULL );
3459     MENU_ExitTracking(hWnd, TRUE);
3460
3461     return ret;
3462 }
3463
3464 /**********************************************************************
3465  *           TrackPopupMenu   (USER32.@)
3466  *
3467  * Like the win32 API, the function return the command ID only if the
3468  * flag TPM_RETURNCMD is on.
3469  *
3470  */
3471 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3472                             INT nReserved, HWND hWnd, const RECT *lpRect )
3473 {
3474     return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3475 }
3476
3477 /***********************************************************************
3478  *           PopupMenuWndProc
3479  *
3480  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3481  */
3482 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3483 {
3484     TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3485
3486     switch(message)
3487     {
3488     case WM_CREATE:
3489         {
3490             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3491             SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3492             return 0;
3493         }
3494
3495     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3496         return MA_NOACTIVATE;
3497
3498     case WM_PAINT:
3499         {
3500             PAINTSTRUCT ps;
3501             BeginPaint( hwnd, &ps );
3502             MENU_DrawPopupMenu( hwnd, ps.hdc,
3503                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3504             EndPaint( hwnd, &ps );
3505             return 0;
3506         }
3507
3508     case WM_PRINTCLIENT:
3509         {
3510             MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3511                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3512             return 0;
3513         }
3514
3515     case WM_ERASEBKGND:
3516         return 1;
3517
3518     case WM_DESTROY:
3519         /* zero out global pointer in case resident popup window was destroyed. */
3520         if (hwnd == top_popup) {
3521             top_popup = 0;
3522             top_popup_hmenu = NULL;
3523         }
3524         break;
3525
3526     case WM_SHOWWINDOW:
3527
3528         if( wParam )
3529         {
3530             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3531         }
3532         else
3533             SetWindowLongPtrW( hwnd, 0, 0 );
3534         break;
3535
3536     case MM_SETMENUHANDLE:
3537         SetWindowLongPtrW( hwnd, 0, wParam );
3538         break;
3539
3540     case MM_GETMENUHANDLE:
3541     case MN_GETHMENU:
3542         return GetWindowLongPtrW( hwnd, 0 );
3543
3544     default:
3545         return DefWindowProcW( hwnd, message, wParam, lParam );
3546     }
3547     return 0;
3548 }
3549
3550
3551 /***********************************************************************
3552  *           MENU_GetMenuBarHeight
3553  *
3554  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3555  */
3556 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3557                               INT orgX, INT orgY )
3558 {
3559     HDC hdc;
3560     RECT rectBar;
3561     LPPOPUPMENU lppop;
3562
3563     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3564
3565     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3566
3567     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3568     SelectObject( hdc, get_menu_font(FALSE));
3569     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3570     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3571     ReleaseDC( hwnd, hdc );
3572     return lppop->Height;
3573 }
3574
3575
3576 /*******************************************************************
3577  *         ChangeMenuA    (USER32.@)
3578  */
3579 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3580                              UINT id, UINT flags )
3581 {
3582     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3583     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3584                                                  id, data );
3585     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3586     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3587                                                 id, data );
3588     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3589                                               flags & MF_BYPOSITION ? pos : id,
3590                                               flags & ~MF_REMOVE );
3591     /* Default: MF_INSERT */
3592     return InsertMenuA( hMenu, pos, flags, id, data );
3593 }
3594
3595
3596 /*******************************************************************
3597  *         ChangeMenuW    (USER32.@)
3598  */
3599 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3600                              UINT id, UINT flags )
3601 {
3602     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3603     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3604                                                  id, data );
3605     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3606     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3607                                                 id, data );
3608     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3609                                               flags & MF_BYPOSITION ? pos : id,
3610                                               flags & ~MF_REMOVE );
3611     /* Default: MF_INSERT */
3612     return InsertMenuW( hMenu, pos, flags, id, data );
3613 }
3614
3615
3616 /*******************************************************************
3617  *         CheckMenuItem    (USER32.@)
3618  */
3619 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3620 {
3621     MENUITEM *item;
3622     DWORD ret;
3623
3624     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3625     ret = item->fState & MF_CHECKED;
3626     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3627     else item->fState &= ~MF_CHECKED;
3628     return ret;
3629 }
3630
3631
3632 /**********************************************************************
3633  *         EnableMenuItem    (USER32.@)
3634  */
3635 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3636 {
3637     UINT    oldflags;
3638     MENUITEM *item;
3639     POPUPMENU *menu;
3640
3641     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3642
3643     /* Get the Popupmenu to access the owner menu */
3644     if (!(menu = MENU_GetMenu(hMenu)))
3645         return (UINT)-1;
3646
3647     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3648         return (UINT)-1;
3649
3650     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3651     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3652
3653     /* If the close item in the system menu change update the close button */
3654     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3655     {
3656         if (menu->hSysMenuOwner != 0)
3657         {
3658             RECT rc;
3659             POPUPMENU* parentMenu;
3660
3661             /* Get the parent menu to access*/
3662             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3663                 return (UINT)-1;
3664
3665             /* Refresh the frame to reflect the change */
3666             WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3667             rc.bottom = 0;
3668             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3669         }
3670     }
3671
3672     return oldflags;
3673 }
3674
3675
3676 /*******************************************************************
3677  *         GetMenuStringA    (USER32.@)
3678  */
3679 INT WINAPI GetMenuStringA(
3680         HMENU hMenu,    /* [in] menuhandle */
3681         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3682         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3683         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3684         UINT wFlags     /* [in] MF_ flags */
3685 ) {
3686     MENUITEM *item;
3687
3688     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3689     if (str && nMaxSiz) str[0] = '\0';
3690     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3691         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3692         return 0;
3693     }
3694     if (!item->text) return 0;
3695     if (!str || !nMaxSiz) return strlenW(item->text);
3696     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3697         str[nMaxSiz-1] = 0;
3698     TRACE("returning %s\n", debugstr_a(str));
3699     return strlen(str);
3700 }
3701
3702
3703 /*******************************************************************
3704  *         GetMenuStringW    (USER32.@)
3705  */
3706 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3707                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3708 {
3709     MENUITEM *item;
3710
3711     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3712     if (str && nMaxSiz) str[0] = '\0';
3713     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3714         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3715         return 0;
3716     }
3717     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3718     if( !(item->text)) {
3719         str[0] = 0;
3720         return 0;
3721     }
3722     lstrcpynW( str, item->text, nMaxSiz );
3723     TRACE("returning %s\n", debugstr_w(str));
3724     return strlenW(str);
3725 }
3726
3727
3728 /**********************************************************************
3729  *         HiliteMenuItem    (USER32.@)
3730  */
3731 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3732                                 UINT wHilite )
3733 {
3734     LPPOPUPMENU menu;
3735     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3736     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3737     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3738     if (menu->FocusedItem == wItemID) return TRUE;
3739     MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3740     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3741     return TRUE;
3742 }
3743
3744
3745 /**********************************************************************
3746  *         GetMenuState    (USER32.@)
3747  */
3748 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3749 {
3750     MENUITEM *item;
3751     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3752     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3753     debug_print_menuitem ("  item: ", item, "");
3754     if (item->fType & MF_POPUP)
3755     {
3756         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3757         if (!menu) return -1;
3758         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3759     }
3760     else
3761     {
3762         /* We used to (from way back then) mask the result to 0xff.  */
3763         /* I don't know why and it seems wrong as the documented */
3764         /* return flag MF_SEPARATOR is outside that mask.  */
3765         return (item->fType | item->fState);
3766     }
3767 }
3768
3769
3770 /**********************************************************************
3771  *         GetMenuItemCount    (USER32.@)
3772  */
3773 INT WINAPI GetMenuItemCount( HMENU hMenu )
3774 {
3775     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3776     if (!menu) return -1;
3777     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3778     return menu->nItems;
3779 }
3780
3781
3782 /**********************************************************************
3783  *         GetMenuItemID    (USER32.@)
3784  */
3785 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3786 {
3787     MENUITEM * lpmi;
3788
3789     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3790     if (lpmi->fType & MF_POPUP) return -1;
3791     return lpmi->wID;
3792
3793 }
3794
3795
3796 /**********************************************************************
3797  *         MENU_mnu2mnuii
3798  *
3799  * Uses flags, id and text ptr, passed by InsertMenu() and
3800  * ModifyMenu() to setup a MenuItemInfo structure.
3801  */
3802 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3803         LPMENUITEMINFOW pmii)
3804 {
3805     ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3806     pmii->cbSize = sizeof( MENUITEMINFOW);
3807     pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3808     /* setting bitmap clears text and vice versa */
3809     if( IS_STRING_ITEM(flags)) {
3810         pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3811         if( !str)
3812             flags |= MF_SEPARATOR;
3813         /* Item beginning with a backspace is a help item */
3814         /* FIXME: wrong place, this is only true in win16 */
3815         else if( *str == '\b') {
3816             flags |= MF_HELP;
3817             str++;
3818         }
3819         pmii->dwTypeData = (LPWSTR)str;
3820     } else if( flags & MFT_BITMAP){
3821         pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3822         pmii->hbmpItem = (HBITMAP)str;
3823     }
3824     if( flags & MF_OWNERDRAW){
3825         pmii->fMask |= MIIM_DATA;
3826         pmii->dwItemData = (ULONG_PTR) str;
3827     }
3828     if( flags & MF_POPUP) {
3829         pmii->fMask |= MIIM_SUBMENU;
3830         pmii->hSubMenu = (HMENU)id;
3831     }
3832     if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3833     pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3834     pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3835     pmii->wID = (UINT)id;
3836 }
3837
3838
3839 /*******************************************************************
3840  *         InsertMenuW    (USER32.@)
3841  */
3842 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3843                          UINT_PTR id, LPCWSTR str )
3844 {
3845     MENUITEM *item;
3846     MENUITEMINFOW mii;
3847
3848     if (IS_STRING_ITEM(flags) && str)
3849         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3850               hMenu, pos, flags, id, debugstr_w(str) );
3851     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3852                hMenu, pos, flags, id, str );
3853
3854     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3855     MENU_mnu2mnuii( flags, id, str, &mii);
3856     if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3857     {
3858         RemoveMenu( hMenu, pos, flags );
3859         return FALSE;
3860     }
3861
3862     item->hCheckBit = item->hUnCheckBit = 0;
3863     return TRUE;
3864 }
3865
3866
3867 /*******************************************************************
3868  *         InsertMenuA    (USER32.@)
3869  */
3870 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3871                          UINT_PTR id, LPCSTR str )
3872 {
3873     BOOL ret = FALSE;
3874
3875     if (IS_STRING_ITEM(flags) && str)
3876     {
3877         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3878         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3879         if (newstr)
3880         {
3881             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3882             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3883             HeapFree( GetProcessHeap(), 0, newstr );
3884         }
3885         return ret;
3886     }
3887     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3888 }
3889
3890
3891 /*******************************************************************
3892  *         AppendMenuA    (USER32.@)
3893  */
3894 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3895                          UINT_PTR id, LPCSTR data )
3896 {
3897     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3898 }
3899
3900
3901 /*******************************************************************
3902  *         AppendMenuW    (USER32.@)
3903  */
3904 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3905                          UINT_PTR id, LPCWSTR data )
3906 {
3907     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3908 }
3909
3910
3911 /**********************************************************************
3912  *         RemoveMenu    (USER32.@)
3913  */
3914 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3915 {
3916     LPPOPUPMENU menu;
3917     MENUITEM *item;
3918
3919     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3920     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3921     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3922
3923       /* Remove item */
3924
3925     MENU_FreeItemData( item );
3926
3927     if (--menu->nItems == 0)
3928     {
3929         HeapFree( GetProcessHeap(), 0, menu->items );
3930         menu->items = NULL;
3931     }
3932     else
3933     {
3934         while(nPos < menu->nItems)
3935         {
3936             *item = *(item+1);
3937             item++;
3938             nPos++;
3939         }
3940         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3941                                    menu->nItems * sizeof(MENUITEM) );
3942     }
3943     return TRUE;
3944 }
3945
3946
3947 /**********************************************************************
3948  *         DeleteMenu    (USER32.@)
3949  */
3950 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3951 {
3952     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3953     if (!item) return FALSE;
3954     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3955       /* nPos is now the position of the item */
3956     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3957     return TRUE;
3958 }
3959
3960
3961 /*******************************************************************
3962  *         ModifyMenuW    (USER32.@)
3963  */
3964 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3965                          UINT_PTR id, LPCWSTR str )
3966 {
3967     MENUITEM *item;
3968     MENUITEMINFOW mii;
3969
3970     if (IS_STRING_ITEM(flags))
3971         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3972     else
3973         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3974
3975     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3976     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3977     MENU_mnu2mnuii( flags, id, str, &mii);
3978     return SetMenuItemInfo_common( item, &mii, TRUE);
3979 }
3980
3981
3982 /*******************************************************************
3983  *         ModifyMenuA    (USER32.@)
3984  */
3985 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3986                          UINT_PTR id, LPCSTR str )
3987 {
3988     BOOL ret = FALSE;
3989
3990     if (IS_STRING_ITEM(flags) && str)
3991     {
3992         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3993         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3994         if (newstr)
3995         {
3996             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3997             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3998             HeapFree( GetProcessHeap(), 0, newstr );
3999         }
4000         return ret;
4001     }
4002     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4003 }
4004
4005
4006 /**********************************************************************
4007  *         CreatePopupMenu    (USER32.@)
4008  */
4009 HMENU WINAPI CreatePopupMenu(void)
4010 {
4011     HMENU hmenu;
4012     POPUPMENU *menu;
4013
4014     if (!(hmenu = CreateMenu())) return 0;
4015     menu = MENU_GetMenu( hmenu );
4016     menu->wFlags |= MF_POPUP;
4017     menu->bTimeToHide = FALSE;
4018     return hmenu;
4019 }
4020
4021
4022 /**********************************************************************
4023  *         GetMenuCheckMarkDimensions    (USER.417)
4024  *         GetMenuCheckMarkDimensions    (USER32.@)
4025  */
4026 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4027 {
4028     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4029 }
4030
4031
4032 /**********************************************************************
4033  *         SetMenuItemBitmaps    (USER32.@)
4034  */
4035 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4036                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4037 {
4038     MENUITEM *item;
4039
4040     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4041
4042     if (!hNewCheck && !hNewUnCheck)
4043     {
4044         item->fState &= ~MF_USECHECKBITMAPS;
4045     }
4046     else  /* Install new bitmaps */
4047     {
4048         item->hCheckBit = hNewCheck;
4049         item->hUnCheckBit = hNewUnCheck;
4050         item->fState |= MF_USECHECKBITMAPS;
4051     }
4052     return TRUE;
4053 }
4054
4055
4056 /**********************************************************************
4057  *         CreateMenu    (USER32.@)
4058  */
4059 HMENU WINAPI CreateMenu(void)
4060 {
4061     HMENU hMenu;
4062     LPPOPUPMENU menu;
4063
4064     if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4065     menu->FocusedItem = NO_SELECTED_ITEM;
4066     menu->bTimeToHide = FALSE;
4067
4068     if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4069
4070     TRACE("return %p\n", hMenu );
4071
4072     return hMenu;
4073 }
4074
4075
4076 /**********************************************************************
4077  *         DestroyMenu    (USER32.@)
4078  */
4079 BOOL WINAPI DestroyMenu( HMENU hMenu )
4080 {
4081     LPPOPUPMENU lppop;
4082
4083     TRACE("(%p)\n", hMenu);
4084
4085     if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4086     if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4087
4088     /* DestroyMenu should not destroy system menu popup owner */
4089     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4090     {
4091         DestroyWindow( lppop->hWnd );
4092         lppop->hWnd = 0;
4093     }
4094
4095     if (lppop->items) /* recursively destroy submenus */
4096     {
4097         int i;
4098         MENUITEM *item = lppop->items;
4099         for (i = lppop->nItems; i > 0; i--, item++)
4100         {
4101             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4102             MENU_FreeItemData( item );
4103         }
4104         HeapFree( GetProcessHeap(), 0, lppop->items );
4105     }
4106     HeapFree( GetProcessHeap(), 0, lppop );
4107     return TRUE;
4108 }
4109
4110
4111 /**********************************************************************
4112  *         GetSystemMenu    (USER32.@)
4113  */
4114 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4115 {
4116     WND *wndPtr = WIN_GetPtr( hWnd );
4117     HMENU retvalue = 0;
4118
4119     if (wndPtr == WND_DESKTOP) return 0;
4120     if (wndPtr == WND_OTHER_PROCESS)
4121     {
4122         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4123     }
4124     else if (wndPtr)
4125     {
4126         if (wndPtr->hSysMenu && bRevert)
4127         {
4128             DestroyMenu(wndPtr->hSysMenu);
4129             wndPtr->hSysMenu = 0;
4130         }
4131
4132         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4133             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4134
4135         if( wndPtr->hSysMenu )
4136         {
4137             POPUPMENU *menu;
4138             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4139
4140             /* Store the dummy sysmenu handle to facilitate the refresh */
4141             /* of the close button if the SC_CLOSE item change */
4142             menu = MENU_GetMenu(retvalue);
4143             if ( menu )
4144                menu->hSysMenuOwner = wndPtr->hSysMenu;
4145         }
4146         WIN_ReleasePtr( wndPtr );
4147     }
4148     return bRevert ? 0 : retvalue;
4149 }
4150
4151
4152 /*******************************************************************
4153  *         SetSystemMenu    (USER32.@)
4154  */
4155 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4156 {
4157     WND *wndPtr = WIN_GetPtr( hwnd );
4158
4159     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4160     {
4161         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4162         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4163         WIN_ReleasePtr( wndPtr );
4164         return TRUE;
4165     }
4166     return FALSE;
4167 }
4168
4169
4170 /**********************************************************************
4171  *         GetMenu    (USER32.@)
4172  */
4173 HMENU WINAPI GetMenu( HWND hWnd )
4174 {
4175     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4176     TRACE("for %p returning %p\n", hWnd, retvalue);
4177     return retvalue;
4178 }
4179
4180 /**********************************************************************
4181  *         GetMenuBarInfo    (USER32.@)
4182  */
4183 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4184 {
4185     FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4186     return FALSE;
4187 }
4188
4189 /**********************************************************************
4190  *         MENU_SetMenu
4191  *
4192  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4193  * SetWindowPos call that would result if SetMenu were called directly.
4194  */
4195 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4196 {
4197     TRACE("(%p, %p);\n", hWnd, hMenu);
4198
4199     if (hMenu && !IsMenu(hMenu))
4200     {
4201         WARN("hMenu %p is not a menu handle\n", hMenu);
4202         return FALSE;
4203     }
4204     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4205         return FALSE;
4206
4207     hWnd = WIN_GetFullHandle( hWnd );
4208     if (GetCapture() == hWnd)
4209         set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
4210
4211     if (hMenu != 0)
4212     {
4213         LPPOPUPMENU lpmenu;
4214
4215         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4216
4217         lpmenu->hWnd = hWnd;
4218         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4219     }
4220     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4221     return TRUE;
4222 }
4223
4224
4225 /**********************************************************************
4226  *         SetMenu    (USER32.@)
4227  */
4228 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4229 {   
4230     if(!MENU_SetMenu(hWnd, hMenu))
4231         return FALSE;
4232  
4233     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4234                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4235     return TRUE;
4236 }
4237
4238
4239 /**********************************************************************
4240  *         GetSubMenu    (USER32.@)
4241  */
4242 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4243 {
4244     MENUITEM * lpmi;
4245
4246     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4247     if (!(lpmi->fType & MF_POPUP)) return 0;
4248     return lpmi->hSubMenu;
4249 }
4250
4251
4252 /**********************************************************************
4253  *         DrawMenuBar    (USER32.@)
4254  */
4255 BOOL WINAPI DrawMenuBar( HWND hWnd )
4256 {
4257     LPPOPUPMENU lppop;
4258     HMENU hMenu = GetMenu(hWnd);
4259
4260     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4261         return FALSE;
4262     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4263
4264     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4265     lppop->hwndOwner = hWnd;
4266     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4267                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4268     return TRUE;
4269 }
4270
4271 /***********************************************************************
4272  *           DrawMenuBarTemp   (USER32.@)
4273  *
4274  * UNDOCUMENTED !!
4275  *
4276  * called by W98SE desk.cpl Control Panel Applet
4277  *
4278  * Not 100% sure about the param names, but close.
4279  */
4280 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4281 {
4282     LPPOPUPMENU lppop;
4283     UINT i,retvalue;
4284     HFONT hfontOld = 0;
4285     BOOL flat_menu = FALSE;
4286
4287     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4288
4289     if (!hMenu)
4290         hMenu = GetMenu(hwnd);
4291
4292     if (!hFont)
4293         hFont = get_menu_font(FALSE);
4294
4295     lppop = MENU_GetMenu( hMenu );
4296     if (lppop == NULL || lprect == NULL)
4297     {
4298         retvalue = GetSystemMetrics(SM_CYMENU);
4299         goto END;
4300     }
4301
4302     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4303
4304     hfontOld = SelectObject( hDC, hFont);
4305
4306     if (lppop->Height == 0)
4307         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4308
4309     lprect->bottom = lprect->top + lppop->Height;
4310
4311     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4312
4313     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4314     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4315     LineTo( hDC, lprect->right, lprect->bottom );
4316
4317     if (lppop->nItems == 0)
4318     {
4319         retvalue = GetSystemMetrics(SM_CYMENU);
4320         goto END;
4321     }
4322
4323     for (i = 0; i < lppop->nItems; i++)
4324     {
4325         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4326                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4327     }
4328     retvalue = lppop->Height;
4329
4330 END:
4331     if (hfontOld) SelectObject (hDC, hfontOld);
4332     return retvalue;
4333 }
4334
4335 /***********************************************************************
4336  *           EndMenu   (USER.187)
4337  *           EndMenu   (USER32.@)
4338  */
4339 BOOL WINAPI EndMenu(void)
4340 {
4341     /* if we are in the menu code, and it is active */
4342     if (!fEndMenu && top_popup)
4343     {
4344         /* terminate the menu handling code */
4345         fEndMenu = TRUE;
4346
4347         /* needs to be posted to wakeup the internal menu handler */
4348         /* which will now terminate the menu, in the event that */
4349         /* the main window was minimized, or lost focus, so we */
4350         /* don't end up with an orphaned menu */
4351         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4352     }
4353     return fEndMenu;
4354 }
4355
4356
4357 /*****************************************************************
4358  *        LoadMenuA   (USER32.@)
4359  */
4360 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4361 {
4362     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4363     if (!hrsrc) return 0;
4364     return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4365 }
4366
4367
4368 /*****************************************************************
4369  *        LoadMenuW   (USER32.@)
4370  */
4371 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4372 {
4373     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4374     if (!hrsrc) return 0;
4375     return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4376 }
4377
4378
4379 /**********************************************************************
4380  *          LoadMenuIndirectW    (USER32.@)
4381  */
4382 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4383 {
4384     HMENU hMenu;
4385     WORD version, offset;
4386     LPCSTR p = template;
4387
4388     version = GET_WORD(p);
4389     p += sizeof(WORD);
4390     TRACE("%p, ver %d\n", template, version );
4391     switch (version)
4392     {
4393       case 0: /* standard format is version of 0 */
4394         offset = GET_WORD(p);
4395         p += sizeof(WORD) + offset;
4396         if (!(hMenu = CreateMenu())) return 0;
4397         if (!MENU_ParseResource( p, hMenu ))
4398           {
4399             DestroyMenu( hMenu );
4400             return 0;
4401           }
4402         return hMenu;
4403       case 1: /* extended format is version of 1 */
4404         offset = GET_WORD(p);
4405         p += sizeof(WORD) + offset;
4406         if (!(hMenu = CreateMenu())) return 0;
4407         if (!MENUEX_ParseResource( p, hMenu))
4408           {
4409             DestroyMenu( hMenu );
4410             return 0;
4411           }
4412         return hMenu;
4413       default:
4414         ERR("version %d not supported.\n", version);
4415         return 0;
4416     }
4417 }
4418
4419
4420 /**********************************************************************
4421  *          LoadMenuIndirectA    (USER32.@)
4422  */
4423 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4424 {
4425     return LoadMenuIndirectW( template );
4426 }
4427
4428
4429 /**********************************************************************
4430  *              IsMenu    (USER32.@)
4431  */
4432 BOOL WINAPI IsMenu(HMENU hmenu)
4433 {
4434     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4435
4436     if (!menu)
4437     {
4438         SetLastError(ERROR_INVALID_MENU_HANDLE);
4439         return FALSE;
4440     }
4441     return TRUE;
4442 }
4443
4444 /**********************************************************************
4445  *              GetMenuItemInfo_common
4446  */
4447
4448 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4449                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4450 {
4451     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4452
4453     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4454
4455     if (!menu) {
4456         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4457         return FALSE;
4458     }
4459     
4460     if( lpmii->fMask & MIIM_TYPE) {
4461         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4462             WARN("invalid combination of fMask bits used\n");
4463             /* this does not happen on Win9x/ME */
4464             SetLastError( ERROR_INVALID_PARAMETER);
4465             return FALSE;
4466         }
4467         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4468         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4469         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4470         if( lpmii->fType & MFT_BITMAP) {
4471             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4472             lpmii->cch = 0;
4473         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4474             /* this does not happen on Win9x/ME */
4475             lpmii->dwTypeData = 0;
4476             lpmii->cch = 0;
4477         }
4478     }
4479
4480     /* copy the text string */
4481     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4482          if( !menu->text ) {
4483                 if(lpmii->dwTypeData && lpmii->cch) {
4484                     lpmii->cch = 0;
4485                     if( unicode)
4486                         *((WCHAR *)lpmii->dwTypeData) = 0;
4487                     else
4488                         *((CHAR *)lpmii->dwTypeData) = 0;
4489                 }
4490          } else {
4491             int len;
4492             if (unicode)
4493             {
4494                 len = strlenW(menu->text);
4495                 if(lpmii->dwTypeData && lpmii->cch)
4496                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4497             }
4498             else
4499             {
4500                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4501                         0, NULL, NULL ) - 1;
4502                 if(lpmii->dwTypeData && lpmii->cch)
4503                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4504                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4505                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4506             }
4507             /* if we've copied a substring we return its length */
4508             if(lpmii->dwTypeData && lpmii->cch)
4509                 if (lpmii->cch <= len + 1)
4510                     lpmii->cch--;
4511                 else
4512                     lpmii->cch = len;
4513             else {
4514                 /* return length of string */
4515                 /* not on Win9x/ME if fType & MFT_BITMAP */
4516                 lpmii->cch = len;
4517             }
4518         }
4519     }
4520
4521     if (lpmii->fMask & MIIM_FTYPE)
4522         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4523
4524     if (lpmii->fMask & MIIM_BITMAP)
4525         lpmii->hbmpItem = menu->hbmpItem;
4526
4527     if (lpmii->fMask & MIIM_STATE)
4528         lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4529
4530     if (lpmii->fMask & MIIM_ID)
4531         lpmii->wID = menu->wID;
4532
4533     if (lpmii->fMask & MIIM_SUBMENU)
4534         lpmii->hSubMenu = menu->hSubMenu;
4535     else {
4536         /* hSubMenu is always cleared 
4537          * (not on Win9x/ME ) */
4538         lpmii->hSubMenu = 0;
4539     }
4540
4541     if (lpmii->fMask & MIIM_CHECKMARKS) {
4542         lpmii->hbmpChecked = menu->hCheckBit;
4543         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4544     }
4545     if (lpmii->fMask & MIIM_DATA)
4546         lpmii->dwItemData = menu->dwItemData;
4547
4548   return TRUE;
4549 }
4550
4551 /**********************************************************************
4552  *              GetMenuItemInfoA    (USER32.@)
4553  */
4554 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4555                                   LPMENUITEMINFOA lpmii)
4556 {
4557     BOOL ret;
4558     MENUITEMINFOA mii;
4559     if( lpmii->cbSize != sizeof( mii) &&
4560             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4561         SetLastError( ERROR_INVALID_PARAMETER);
4562         return FALSE;
4563     }
4564     memcpy( &mii, lpmii, lpmii->cbSize);
4565     mii.cbSize = sizeof( mii);
4566     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4567                                     (LPMENUITEMINFOW)&mii, FALSE);
4568     mii.cbSize = lpmii->cbSize;
4569     memcpy( lpmii, &mii, mii.cbSize);
4570     return ret;
4571 }
4572
4573 /**********************************************************************
4574  *              GetMenuItemInfoW    (USER32.@)
4575  */
4576 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4577                                   LPMENUITEMINFOW lpmii)
4578 {
4579     BOOL ret;
4580     MENUITEMINFOW mii;
4581     if( lpmii->cbSize != sizeof( mii) &&
4582             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4583         SetLastError( ERROR_INVALID_PARAMETER);
4584         return FALSE;
4585     }
4586     memcpy( &mii, lpmii, lpmii->cbSize);
4587     mii.cbSize = sizeof( mii);
4588     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4589     mii.cbSize = lpmii->cbSize;
4590     memcpy( lpmii, &mii, mii.cbSize);
4591     return ret;
4592 }
4593
4594
4595 /* set a menu item text from a ASCII or Unicode string */
4596 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4597 {
4598     if (!text)
4599         menu->text = NULL;
4600     else if (unicode)
4601     {
4602         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4603             strcpyW( menu->text, text );
4604     }
4605     else
4606     {
4607         LPCSTR str = (LPCSTR)text;
4608         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4609         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4610             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4611     }
4612 }
4613
4614
4615 /**********************************************************************
4616  *              MENU_depth
4617  *
4618  * detect if there are loops in the menu tree (or the depth is too large)
4619  */
4620 static int MENU_depth( POPUPMENU *pmenu, int depth)
4621 {
4622     int i;
4623     MENUITEM *item;
4624     int subdepth;
4625
4626     depth++;
4627     if( depth > MAXMENUDEPTH) return depth;
4628     item = pmenu->items;
4629     subdepth = depth;
4630     for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4631         POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4632         if( psubmenu){
4633             int bdepth = MENU_depth( psubmenu, depth);
4634             if( bdepth > subdepth) subdepth = bdepth;
4635         }
4636         if( subdepth > MAXMENUDEPTH)
4637             TRACE("<- hmenu %p\n", item->hSubMenu);
4638     }
4639     return subdepth;
4640 }
4641
4642
4643 /**********************************************************************
4644  *              SetMenuItemInfo_common
4645  *
4646  * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4647  * MIIM_BITMAP and MIIM_STRING flags instead.
4648  */
4649
4650 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4651                                        const MENUITEMINFOW *lpmii,
4652                                        BOOL unicode)
4653 {
4654     if (!menu) return FALSE;
4655
4656     debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4657
4658     if (lpmii->fMask & MIIM_FTYPE ) {
4659         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4660         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4661     }
4662     if (lpmii->fMask & MIIM_STRING ) {
4663         /* free the string when used */
4664         HeapFree(GetProcessHeap(), 0, menu->text);
4665         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4666     }
4667
4668     if (lpmii->fMask & MIIM_STATE)
4669          /* Other menu items having MFS_DEFAULT are not converted
4670            to normal items */
4671          menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4672
4673     if (lpmii->fMask & MIIM_ID)
4674         menu->wID = lpmii->wID;
4675
4676     if (lpmii->fMask & MIIM_SUBMENU) {
4677         menu->hSubMenu = lpmii->hSubMenu;
4678         if (menu->hSubMenu) {
4679             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4680             if (subMenu) {
4681                 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4682                     ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4683                     menu->hSubMenu = 0;
4684                     return FALSE;
4685                 }
4686                 subMenu->wFlags |= MF_POPUP;
4687                 menu->fType |= MF_POPUP;
4688             } else {
4689                 SetLastError( ERROR_INVALID_PARAMETER);
4690                 return FALSE;
4691             }
4692         }
4693         else
4694             menu->fType &= ~MF_POPUP;
4695     }
4696
4697     if (lpmii->fMask & MIIM_CHECKMARKS)
4698     {
4699         menu->hCheckBit = lpmii->hbmpChecked;
4700         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4701     }
4702     if (lpmii->fMask & MIIM_DATA)
4703         menu->dwItemData = lpmii->dwItemData;
4704
4705     if (lpmii->fMask & MIIM_BITMAP)
4706         menu->hbmpItem = lpmii->hbmpItem;
4707
4708     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4709         menu->fType |= MFT_SEPARATOR;
4710
4711     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4712     return TRUE;
4713 }
4714
4715 /**********************************************************************
4716  *              MENU_NormalizeMenuItemInfoStruct
4717  *
4718  * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4719  * check, copy and extend the MENUITEMINFO struct from the version that the application
4720  * supplied to the version used by wine source. */
4721 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4722                                               MENUITEMINFOW *pmii_out )
4723 {
4724     /* do we recognize the size? */
4725     if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4726             pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4727         SetLastError( ERROR_INVALID_PARAMETER);
4728         return FALSE;
4729     }
4730     /* copy the fields that we have */
4731     memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4732     /* if the hbmpItem member is missing then extend */
4733     if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4734         pmii_out->cbSize = sizeof( MENUITEMINFOW);
4735         pmii_out->hbmpItem = NULL;
4736     }
4737     /* test for invalid bit combinations */
4738     if( (pmii_out->fMask & MIIM_TYPE &&
4739          pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4740         (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4741         WARN("invalid combination of fMask bits used\n");
4742         /* this does not happen on Win9x/ME */
4743         SetLastError( ERROR_INVALID_PARAMETER);
4744         return FALSE;
4745     }
4746     /* convert old style (MIIM_TYPE) to the new */
4747     if( pmii_out->fMask & MIIM_TYPE){
4748         pmii_out->fMask |= MIIM_FTYPE;
4749         if( IS_STRING_ITEM(pmii_out->fType)){
4750             pmii_out->fMask |= MIIM_STRING;
4751         } else if( (pmii_out->fType) & MFT_BITMAP){
4752             pmii_out->fMask |= MIIM_BITMAP;
4753             pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4754         }
4755     }
4756     return TRUE;
4757 }
4758
4759 /**********************************************************************
4760  *              SetMenuItemInfoA    (USER32.@)
4761  */
4762 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4763                                  const MENUITEMINFOA *lpmii)
4764 {
4765     MENUITEMINFOW mii;
4766
4767     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4768
4769     if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4770
4771     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4772                                   &mii, FALSE);
4773 }
4774
4775 /**********************************************************************
4776  *              SetMenuItemInfoW    (USER32.@)
4777  */
4778 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4779                                  const MENUITEMINFOW *lpmii)
4780 {
4781     MENUITEMINFOW mii;
4782
4783     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4784
4785     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4786     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4787                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4788 }
4789
4790 /**********************************************************************
4791  *              SetMenuDefaultItem    (USER32.@)
4792  *
4793  */
4794 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4795 {
4796         UINT i;
4797         POPUPMENU *menu;
4798         MENUITEM *item;
4799
4800         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4801
4802         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4803
4804         /* reset all default-item flags */
4805         item = menu->items;
4806         for (i = 0; i < menu->nItems; i++, item++)
4807         {
4808             item->fState &= ~MFS_DEFAULT;
4809         }
4810
4811         /* no default item */
4812         if ( -1 == uItem)
4813         {
4814             return TRUE;
4815         }
4816
4817         item = menu->items;
4818         if ( bypos )
4819         {
4820             if ( uItem >= menu->nItems ) return FALSE;
4821             item[uItem].fState |= MFS_DEFAULT;
4822             return TRUE;
4823         }
4824         else
4825         {
4826             for (i = 0; i < menu->nItems; i++, item++)
4827             {
4828                 if (item->wID == uItem)
4829                 {
4830                      item->fState |= MFS_DEFAULT;
4831                      return TRUE;
4832                 }
4833             }
4834
4835         }
4836         return FALSE;
4837 }
4838
4839 /**********************************************************************
4840  *              GetMenuDefaultItem    (USER32.@)
4841  */
4842 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4843 {
4844         POPUPMENU *menu;
4845         MENUITEM * item;
4846         UINT i = 0;
4847
4848         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4849
4850         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4851
4852         /* find default item */
4853         item = menu->items;
4854
4855         /* empty menu */
4856         if (! item) return -1;
4857
4858         while ( !( item->fState & MFS_DEFAULT ) )
4859         {
4860             i++; item++;
4861             if  (i >= menu->nItems ) return -1;
4862         }
4863
4864         /* default: don't return disabled items */
4865         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4866
4867         /* search rekursiv when needed */
4868         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4869         {
4870             UINT ret;
4871             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4872             if ( -1 != ret ) return ret;
4873
4874             /* when item not found in submenu, return the popup item */
4875         }
4876         return ( bypos ) ? i : item->wID;
4877
4878 }
4879
4880
4881 /**********************************************************************
4882  *              InsertMenuItemA    (USER32.@)
4883  */
4884 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4885                                 const MENUITEMINFOA *lpmii)
4886 {
4887     MENUITEM *item;
4888     MENUITEMINFOW mii;
4889
4890     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4891
4892     if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4893
4894     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4895     return SetMenuItemInfo_common(item, &mii, FALSE);
4896 }
4897
4898
4899 /**********************************************************************
4900  *              InsertMenuItemW    (USER32.@)
4901  */
4902 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4903                                 const MENUITEMINFOW *lpmii)
4904 {
4905     MENUITEM *item;
4906     MENUITEMINFOW mii;
4907
4908     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4909
4910     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4911
4912     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4913     return SetMenuItemInfo_common(item, &mii, TRUE);
4914 }
4915
4916 /**********************************************************************
4917  *              CheckMenuRadioItem    (USER32.@)
4918  */
4919
4920 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4921                                    UINT first, UINT last, UINT check,
4922                                    UINT bypos)
4923 {
4924     BOOL done = FALSE;
4925     UINT i;
4926     MENUITEM *mi_first = NULL, *mi_check;
4927     HMENU m_first, m_check;
4928
4929     for (i = first; i <= last; i++)
4930     {
4931         UINT pos = i;
4932
4933         if (!mi_first)
4934         {
4935             m_first = hMenu;
4936             mi_first = MENU_FindItem(&m_first, &pos, bypos);
4937             if (!mi_first) continue;
4938             mi_check = mi_first;
4939             m_check = m_first;
4940         }
4941         else
4942         {
4943             m_check = hMenu;
4944             mi_check = MENU_FindItem(&m_check, &pos, bypos);
4945             if (!mi_check) continue;
4946         }
4947
4948         if (m_first != m_check) continue;
4949         if (mi_check->fType == MFT_SEPARATOR) continue;
4950
4951         if (i == check)
4952         {
4953             mi_check->fType |= MFT_RADIOCHECK;
4954             mi_check->fState |= MFS_CHECKED;
4955             done = TRUE;
4956         }
4957         else
4958         {
4959             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4960             mi_check->fState &= ~MFS_CHECKED;
4961         }
4962     }
4963
4964     return done;
4965 }
4966
4967
4968 /**********************************************************************
4969  *              GetMenuItemRect    (USER32.@)
4970  *
4971  *      ATTENTION: Here, the returned values in rect are the screen
4972  *                 coordinates of the item just like if the menu was
4973  *                 always on the upper left side of the application.
4974  *
4975  */
4976 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4977                                  LPRECT rect)
4978 {
4979      POPUPMENU *itemMenu;
4980      MENUITEM *item;
4981      HWND referenceHwnd;
4982
4983      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4984
4985      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4986      referenceHwnd = hwnd;
4987
4988      if(!hwnd)
4989      {
4990          itemMenu = MENU_GetMenu(hMenu);
4991          if (itemMenu == NULL)
4992              return FALSE;
4993
4994          if(itemMenu->hWnd == 0)
4995              return FALSE;
4996          referenceHwnd = itemMenu->hWnd;
4997      }
4998
4999      if ((rect == NULL) || (item == NULL))
5000          return FALSE;
5001
5002      *rect = item->rect;
5003
5004      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5005
5006      return TRUE;
5007 }
5008
5009 /**********************************************************************
5010  *              SetMenuInfo    (USER32.@)
5011  *
5012  * FIXME
5013  *      actually use the items to draw the menu
5014  *      (recalculate and/or redraw)
5015  */
5016 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5017 {
5018     POPUPMENU *menu;
5019     if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5020
5021     if (lpmi->fMask & MIM_BACKGROUND)
5022         menu->hbrBack = lpmi->hbrBack;
5023
5024     if (lpmi->fMask & MIM_HELPID)
5025         menu->dwContextHelpID = lpmi->dwContextHelpID;
5026
5027     if (lpmi->fMask & MIM_MAXHEIGHT)
5028         menu->cyMax = lpmi->cyMax;
5029
5030     if (lpmi->fMask & MIM_MENUDATA)
5031         menu->dwMenuData = lpmi->dwMenuData;
5032
5033     if (lpmi->fMask & MIM_STYLE)
5034         menu->dwStyle = lpmi->dwStyle;
5035
5036     if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5037         int i;
5038         MENUITEM *item = menu->items;
5039         for( i = menu->nItems; i; i--, item++)
5040             if( item->fType & MF_POPUP)
5041                 menu_SetMenuInfo( item->hSubMenu, lpmi);
5042     }
5043     return TRUE;
5044 }
5045
5046 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5047 {
5048     TRACE("(%p %p)\n", hMenu, lpmi);
5049     if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5050         if( lpmi->fMask & MIM_STYLE) {
5051             if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5052             if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5053             if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5054         }
5055         return TRUE;
5056     }
5057     SetLastError( ERROR_INVALID_PARAMETER);
5058     return FALSE;
5059 }
5060
5061 /**********************************************************************
5062  *              GetMenuInfo    (USER32.@)
5063  *
5064  *  NOTES
5065  *      win98/NT5.0
5066  *
5067  */
5068 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5069 {   POPUPMENU *menu;
5070
5071     TRACE("(%p %p)\n", hMenu, lpmi);
5072
5073     if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5074     {
5075
5076         if (lpmi->fMask & MIM_BACKGROUND)
5077             lpmi->hbrBack = menu->hbrBack;
5078
5079         if (lpmi->fMask & MIM_HELPID)
5080             lpmi->dwContextHelpID = menu->dwContextHelpID;
5081
5082         if (lpmi->fMask & MIM_MAXHEIGHT)
5083             lpmi->cyMax = menu->cyMax;
5084
5085         if (lpmi->fMask & MIM_MENUDATA)
5086             lpmi->dwMenuData = menu->dwMenuData;
5087
5088         if (lpmi->fMask & MIM_STYLE)
5089             lpmi->dwStyle = menu->dwStyle;
5090
5091         return TRUE;
5092     }
5093     SetLastError( ERROR_INVALID_PARAMETER);
5094     return FALSE;
5095 }
5096
5097
5098 /**********************************************************************
5099  *         SetMenuContextHelpId    (USER32.@)
5100  */
5101 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5102 {
5103     LPPOPUPMENU menu;
5104
5105     TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5106
5107     if ((menu = MENU_GetMenu(hMenu)))
5108     {
5109         menu->dwContextHelpID = dwContextHelpID;
5110         return TRUE;
5111     }
5112     return FALSE;
5113 }
5114
5115
5116 /**********************************************************************
5117  *         GetMenuContextHelpId    (USER32.@)
5118  */
5119 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5120 {
5121     LPPOPUPMENU menu;
5122
5123     TRACE("(%p)\n", hMenu);
5124
5125     if ((menu = MENU_GetMenu(hMenu)))
5126     {
5127         return menu->dwContextHelpID;
5128     }
5129     return 0;
5130 }
5131
5132 /**********************************************************************
5133  *         MenuItemFromPoint    (USER32.@)
5134  */
5135 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5136 {
5137     POPUPMENU *menu = MENU_GetMenu(hMenu);
5138     UINT pos;
5139
5140     /*FIXME: Do we have to handle hWnd here? */
5141     if (!menu) return -1;
5142     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5143     return pos;
5144 }
5145
5146
5147 /**********************************************************************
5148  *           translate_accelerator
5149  */
5150 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5151                                    BYTE fVirt, WORD key, WORD cmd )
5152 {
5153     INT mask = 0;
5154     UINT mesg = 0;
5155
5156     if (wParam != key) return FALSE;
5157
5158     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5159     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5160     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5161
5162     if (message == WM_CHAR || message == WM_SYSCHAR)
5163     {
5164         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5165         {
5166             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5167             goto found;
5168         }
5169     }
5170     else
5171     {
5172         if(fVirt & FVIRTKEY)
5173         {
5174             TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5175                           wParam, 0xff & HIWORD(lParam));
5176
5177             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5178             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5179         }
5180         else
5181         {
5182             if (!(lParam & 0x01000000))  /* no special_key */
5183             {
5184                 if ((fVirt & FALT) && (lParam & 0x20000000))
5185                 {                              /* ^^ ALT pressed */
5186                     TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5187                     goto found;
5188                 }
5189             }
5190         }
5191     }
5192     return FALSE;
5193
5194  found:
5195     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5196         mesg = 1;
5197     else
5198     {
5199         HMENU hMenu, hSubMenu, hSysMenu;
5200         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5201
5202         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5203         hSysMenu = get_win_sys_menu( hWnd );
5204
5205         /* find menu item and ask application to initialize it */
5206         /* 1. in the system menu */
5207         hSubMenu = hSysMenu;
5208         nPos = cmd;
5209         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5210         {
5211             if (GetCapture())
5212                 mesg = 2;
5213             if (!IsWindowEnabled(hWnd))
5214                 mesg = 3;
5215             else
5216             {
5217                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5218                 if(hSubMenu != hSysMenu)
5219                 {
5220                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5221                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5222                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5223                 }
5224                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5225             }
5226         }
5227         else /* 2. in the window's menu */
5228         {
5229             hSubMenu = hMenu;
5230             nPos = cmd;
5231             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5232             {
5233                 if (GetCapture())
5234                     mesg = 2;
5235                 if (!IsWindowEnabled(hWnd))
5236                     mesg = 3;
5237                 else
5238                 {
5239                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5240                     if(hSubMenu != hMenu)
5241                     {
5242                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5243                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5244                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5245                     }
5246                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5247                 }
5248             }
5249         }
5250
5251         if (mesg == 0)
5252         {
5253             if (uSysStat != (UINT)-1)
5254             {
5255                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5256                     mesg=4;
5257                 else
5258                     mesg=WM_SYSCOMMAND;
5259             }
5260             else
5261             {
5262                 if (uStat != (UINT)-1)
5263                 {
5264                     if (IsIconic(hWnd))
5265                         mesg=5;
5266                     else
5267                     {
5268                         if (uStat & (MF_DISABLED|MF_GRAYED))
5269                             mesg=6;
5270                         else
5271                             mesg=WM_COMMAND;
5272                     }
5273                 }
5274                 else
5275                     mesg=WM_COMMAND;
5276             }
5277         }
5278     }
5279
5280     if( mesg==WM_COMMAND )
5281     {
5282         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5283         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5284     }
5285     else if( mesg==WM_SYSCOMMAND )
5286     {
5287         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5288         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5289     }
5290     else
5291     {
5292         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5293          *   #0: unknown (please report!)
5294          *   #1: for WM_KEYUP,WM_SYSKEYUP
5295          *   #2: mouse is captured
5296          *   #3: window is disabled
5297          *   #4: it's a disabled system menu option
5298          *   #5: it's a menu option, but window is iconic
5299          *   #6: it's a menu option, but disabled
5300          */
5301         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5302         if(mesg==0)
5303             ERR_(accel)(" unknown reason - please report!\n");
5304     }
5305     return TRUE;
5306 }
5307
5308 /**********************************************************************
5309  *      TranslateAcceleratorA     (USER32.@)
5310  *      TranslateAccelerator      (USER32.@)
5311  */
5312 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5313 {
5314     switch (msg->message)
5315     {
5316     case WM_KEYDOWN:
5317     case WM_SYSKEYDOWN:
5318         return TranslateAcceleratorW( hWnd, hAccel, msg );
5319
5320     case WM_CHAR:
5321     case WM_SYSCHAR:
5322         {
5323             MSG msgW = *msg;
5324             char ch = LOWORD(msg->wParam);
5325             WCHAR wch;
5326             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5327             msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5328             return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5329         }
5330
5331     default:
5332         return 0;
5333     }
5334 }
5335
5336 /**********************************************************************
5337  *      TranslateAcceleratorW     (USER32.@)
5338  */
5339 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5340 {
5341     ACCEL data[32], *ptr = data;
5342     int i, count;
5343
5344     if (!hWnd) return 0;
5345
5346     if (msg->message != WM_KEYDOWN &&
5347         msg->message != WM_SYSKEYDOWN &&
5348         msg->message != WM_CHAR &&
5349         msg->message != WM_SYSCHAR)
5350         return 0;
5351
5352     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5353                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5354
5355     if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5356     if (count > sizeof(data)/sizeof(data[0]))
5357     {
5358         if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5359     }
5360     count = CopyAcceleratorTableW( hAccel, ptr, count );
5361     for (i = 0; i < count; i++)
5362     {
5363         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5364                                    ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5365             break;
5366     }
5367     if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5368     return (i < count);
5369 }