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