oleaut32: Fix SLTG parser so that dual interfaces are returned as IDispatch first.
[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     case MN_GETHMENU:
3513         return GetWindowLongPtrW( hwnd, 0 );
3514
3515     default:
3516         return DefWindowProcW( hwnd, message, wParam, lParam );
3517     }
3518     return 0;
3519 }
3520
3521
3522 /***********************************************************************
3523  *           MENU_GetMenuBarHeight
3524  *
3525  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3526  */
3527 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3528                               INT orgX, INT orgY )
3529 {
3530     HDC hdc;
3531     RECT rectBar;
3532     LPPOPUPMENU lppop;
3533
3534     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3535
3536     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3537
3538     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3539     SelectObject( hdc, get_menu_font(FALSE));
3540     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3541     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3542     ReleaseDC( hwnd, hdc );
3543     return lppop->Height;
3544 }
3545
3546
3547 /*******************************************************************
3548  *         ChangeMenuA    (USER32.@)
3549  */
3550 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3551                              UINT id, UINT flags )
3552 {
3553     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3554     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3555                                                  id, data );
3556     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3557     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3558                                                 id, data );
3559     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3560                                               flags & MF_BYPOSITION ? pos : id,
3561                                               flags & ~MF_REMOVE );
3562     /* Default: MF_INSERT */
3563     return InsertMenuA( hMenu, pos, flags, id, data );
3564 }
3565
3566
3567 /*******************************************************************
3568  *         ChangeMenuW    (USER32.@)
3569  */
3570 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3571                              UINT id, UINT flags )
3572 {
3573     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3574     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3575                                                  id, data );
3576     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3577     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3578                                                 id, data );
3579     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3580                                               flags & MF_BYPOSITION ? pos : id,
3581                                               flags & ~MF_REMOVE );
3582     /* Default: MF_INSERT */
3583     return InsertMenuW( hMenu, pos, flags, id, data );
3584 }
3585
3586
3587 /*******************************************************************
3588  *         CheckMenuItem    (USER32.@)
3589  */
3590 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3591 {
3592     MENUITEM *item;
3593     DWORD ret;
3594
3595     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3596     ret = item->fState & MF_CHECKED;
3597     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3598     else item->fState &= ~MF_CHECKED;
3599     return ret;
3600 }
3601
3602
3603 /**********************************************************************
3604  *         EnableMenuItem    (USER32.@)
3605  */
3606 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3607 {
3608     UINT    oldflags;
3609     MENUITEM *item;
3610     POPUPMENU *menu;
3611
3612     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3613
3614     /* Get the Popupmenu to access the owner menu */
3615     if (!(menu = MENU_GetMenu(hMenu)))
3616         return (UINT)-1;
3617
3618     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3619         return (UINT)-1;
3620
3621     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3622     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3623
3624     /* If the close item in the system menu change update the close button */
3625     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3626     {
3627         if (menu->hSysMenuOwner != 0)
3628         {
3629             RECT rc;
3630             POPUPMENU* parentMenu;
3631
3632             /* Get the parent menu to access*/
3633             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3634                 return (UINT)-1;
3635
3636             /* Refresh the frame to reflect the change */
3637             GetWindowRect(parentMenu->hWnd, &rc);
3638             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3639             rc.bottom = 0;
3640             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3641         }
3642     }
3643
3644     return oldflags;
3645 }
3646
3647
3648 /*******************************************************************
3649  *         GetMenuStringA    (USER32.@)
3650  */
3651 INT WINAPI GetMenuStringA(
3652         HMENU hMenu,    /* [in] menuhandle */
3653         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3654         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3655         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3656         UINT wFlags     /* [in] MF_ flags */
3657 ) {
3658     MENUITEM *item;
3659
3660     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3661     if (str && nMaxSiz) str[0] = '\0';
3662     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3663         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3664         return 0;
3665     }
3666     if (!item->text) return 0;
3667     if (!str || !nMaxSiz) return strlenW(item->text);
3668     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3669         str[nMaxSiz-1] = 0;
3670     TRACE("returning %s\n", debugstr_a(str));
3671     return strlen(str);
3672 }
3673
3674
3675 /*******************************************************************
3676  *         GetMenuStringW    (USER32.@)
3677  */
3678 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3679                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3680 {
3681     MENUITEM *item;
3682
3683     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3684     if (str && nMaxSiz) str[0] = '\0';
3685     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3686         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3687         return 0;
3688     }
3689     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3690     if( !(item->text)) {
3691         str[0] = 0;
3692         return 0;
3693     }
3694     lstrcpynW( str, item->text, nMaxSiz );
3695     TRACE("returning %s\n", debugstr_w(str));
3696     return strlenW(str);
3697 }
3698
3699
3700 /**********************************************************************
3701  *         HiliteMenuItem    (USER32.@)
3702  */
3703 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3704                                 UINT wHilite )
3705 {
3706     LPPOPUPMENU menu;
3707     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3708     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3709     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3710     if (menu->FocusedItem == wItemID) return TRUE;
3711     MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3712     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3713     return TRUE;
3714 }
3715
3716
3717 /**********************************************************************
3718  *         GetMenuState    (USER32.@)
3719  */
3720 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3721 {
3722     MENUITEM *item;
3723     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3724     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3725     debug_print_menuitem ("  item: ", item, "");
3726     if (item->fType & MF_POPUP)
3727     {
3728         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3729         if (!menu) return -1;
3730         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3731     }
3732     else
3733     {
3734         /* We used to (from way back then) mask the result to 0xff.  */
3735         /* I don't know why and it seems wrong as the documented */
3736         /* return flag MF_SEPARATOR is outside that mask.  */
3737         return (item->fType | item->fState);
3738     }
3739 }
3740
3741
3742 /**********************************************************************
3743  *         GetMenuItemCount    (USER32.@)
3744  */
3745 INT WINAPI GetMenuItemCount( HMENU hMenu )
3746 {
3747     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3748     if (!menu) return -1;
3749     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3750     return menu->nItems;
3751 }
3752
3753
3754 /**********************************************************************
3755  *         GetMenuItemID    (USER32.@)
3756  */
3757 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3758 {
3759     MENUITEM * lpmi;
3760
3761     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3762     if (lpmi->fType & MF_POPUP) return -1;
3763     return lpmi->wID;
3764
3765 }
3766
3767
3768 /**********************************************************************
3769  *         MENU_mnu2mnuii
3770  *
3771  * Uses flags, id and text ptr, passed by InsertMenu() and
3772  * ModifyMenu() to setup a MenuItemInfo structure.
3773  */
3774 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3775         LPMENUITEMINFOW pmii)
3776 {
3777     ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3778     pmii->cbSize = sizeof( MENUITEMINFOW);
3779     pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3780     /* setting bitmap clears text and vice versa */
3781     if( IS_STRING_ITEM(flags)) {
3782         pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3783         if( !str)
3784             flags |= MF_SEPARATOR;
3785         /* Item beginning with a backspace is a help item */
3786         /* FIXME: wrong place, this is only true in win16 */
3787         else if( *str == '\b') {
3788             flags |= MF_HELP;
3789             str++;
3790         }
3791         pmii->dwTypeData = (LPWSTR)str;
3792     } else if( flags & MFT_BITMAP){
3793         pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3794         pmii->hbmpItem = HBITMAP_32(LOWORD(str));
3795     }
3796     if( flags & MF_OWNERDRAW){
3797         pmii->fMask |= MIIM_DATA;
3798         pmii->dwItemData = (ULONG_PTR) str;
3799     }
3800     if( flags & MF_POPUP) {
3801         pmii->fMask |= MIIM_SUBMENU;
3802         pmii->hSubMenu = (HMENU)id;
3803     }
3804     if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3805     pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3806     pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3807     pmii->wID = (UINT)id;
3808 }
3809
3810
3811 /*******************************************************************
3812  *         InsertMenuW    (USER32.@)
3813  */
3814 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3815                          UINT_PTR id, LPCWSTR str )
3816 {
3817     MENUITEM *item;
3818     MENUITEMINFOW mii;
3819
3820     if (IS_STRING_ITEM(flags) && str)
3821         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3822               hMenu, pos, flags, id, debugstr_w(str) );
3823     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3824                hMenu, pos, flags, id, str );
3825
3826     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3827     MENU_mnu2mnuii( flags, id, str, &mii);
3828     if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3829     {
3830         RemoveMenu( hMenu, pos, flags );
3831         return FALSE;
3832     }
3833
3834     item->hCheckBit = item->hUnCheckBit = 0;
3835     return TRUE;
3836 }
3837
3838
3839 /*******************************************************************
3840  *         InsertMenuA    (USER32.@)
3841  */
3842 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3843                          UINT_PTR id, LPCSTR str )
3844 {
3845     BOOL ret = FALSE;
3846
3847     if (IS_STRING_ITEM(flags) && str)
3848     {
3849         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3850         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3851         if (newstr)
3852         {
3853             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3854             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3855             HeapFree( GetProcessHeap(), 0, newstr );
3856         }
3857         return ret;
3858     }
3859     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3860 }
3861
3862
3863 /*******************************************************************
3864  *         AppendMenuA    (USER32.@)
3865  */
3866 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3867                          UINT_PTR id, LPCSTR data )
3868 {
3869     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3870 }
3871
3872
3873 /*******************************************************************
3874  *         AppendMenuW    (USER32.@)
3875  */
3876 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3877                          UINT_PTR id, LPCWSTR data )
3878 {
3879     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3880 }
3881
3882
3883 /**********************************************************************
3884  *         RemoveMenu    (USER32.@)
3885  */
3886 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3887 {
3888     LPPOPUPMENU menu;
3889     MENUITEM *item;
3890
3891     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3892     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3893     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3894
3895       /* Remove item */
3896
3897     MENU_FreeItemData( item );
3898
3899     if (--menu->nItems == 0)
3900     {
3901         HeapFree( GetProcessHeap(), 0, menu->items );
3902         menu->items = NULL;
3903     }
3904     else
3905     {
3906         while(nPos < menu->nItems)
3907         {
3908             *item = *(item+1);
3909             item++;
3910             nPos++;
3911         }
3912         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3913                                    menu->nItems * sizeof(MENUITEM) );
3914     }
3915     return TRUE;
3916 }
3917
3918
3919 /**********************************************************************
3920  *         DeleteMenu    (USER32.@)
3921  */
3922 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3923 {
3924     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3925     if (!item) return FALSE;
3926     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3927       /* nPos is now the position of the item */
3928     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3929     return TRUE;
3930 }
3931
3932
3933 /*******************************************************************
3934  *         ModifyMenuW    (USER32.@)
3935  */
3936 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3937                          UINT_PTR id, LPCWSTR str )
3938 {
3939     MENUITEM *item;
3940     MENUITEMINFOW mii;
3941
3942     if (IS_STRING_ITEM(flags))
3943         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3944     else
3945         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3946
3947     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3948     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3949     MENU_mnu2mnuii( flags, id, str, &mii);
3950     return SetMenuItemInfo_common( item, &mii, TRUE);
3951 }
3952
3953
3954 /*******************************************************************
3955  *         ModifyMenuA    (USER32.@)
3956  */
3957 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3958                          UINT_PTR id, LPCSTR str )
3959 {
3960     BOOL ret = FALSE;
3961
3962     if (IS_STRING_ITEM(flags) && str)
3963     {
3964         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3965         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3966         if (newstr)
3967         {
3968             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3969             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3970             HeapFree( GetProcessHeap(), 0, newstr );
3971         }
3972         return ret;
3973     }
3974     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3975 }
3976
3977
3978 /**********************************************************************
3979  *         CreatePopupMenu    (USER32.@)
3980  */
3981 HMENU WINAPI CreatePopupMenu(void)
3982 {
3983     HMENU hmenu;
3984     POPUPMENU *menu;
3985
3986     if (!(hmenu = CreateMenu())) return 0;
3987     menu = MENU_GetMenu( hmenu );
3988     menu->wFlags |= MF_POPUP;
3989     menu->bTimeToHide = FALSE;
3990     return hmenu;
3991 }
3992
3993
3994 /**********************************************************************
3995  *         GetMenuCheckMarkDimensions    (USER.417)
3996  *         GetMenuCheckMarkDimensions    (USER32.@)
3997  */
3998 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3999 {
4000     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4001 }
4002
4003
4004 /**********************************************************************
4005  *         SetMenuItemBitmaps    (USER32.@)
4006  */
4007 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4008                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4009 {
4010     MENUITEM *item;
4011
4012     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4013
4014     if (!hNewCheck && !hNewUnCheck)
4015     {
4016         item->fState &= ~MF_USECHECKBITMAPS;
4017     }
4018     else  /* Install new bitmaps */
4019     {
4020         item->hCheckBit = hNewCheck;
4021         item->hUnCheckBit = hNewUnCheck;
4022         item->fState |= MF_USECHECKBITMAPS;
4023     }
4024     return TRUE;
4025 }
4026
4027
4028 /**********************************************************************
4029  *         CreateMenu    (USER32.@)
4030  */
4031 HMENU WINAPI CreateMenu(void)
4032 {
4033     HMENU hMenu;
4034     LPPOPUPMENU menu;
4035
4036     if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4037     menu->FocusedItem = NO_SELECTED_ITEM;
4038     menu->bTimeToHide = FALSE;
4039
4040     if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4041
4042     TRACE("return %p\n", hMenu );
4043
4044     return hMenu;
4045 }
4046
4047
4048 /**********************************************************************
4049  *         DestroyMenu    (USER32.@)
4050  */
4051 BOOL WINAPI DestroyMenu( HMENU hMenu )
4052 {
4053     LPPOPUPMENU lppop;
4054
4055     TRACE("(%p)\n", hMenu);
4056
4057     if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4058     if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4059
4060     /* DestroyMenu should not destroy system menu popup owner */
4061     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4062     {
4063         DestroyWindow( lppop->hWnd );
4064         lppop->hWnd = 0;
4065     }
4066
4067     if (lppop->items) /* recursively destroy submenus */
4068     {
4069         int i;
4070         MENUITEM *item = lppop->items;
4071         for (i = lppop->nItems; i > 0; i--, item++)
4072         {
4073             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4074             MENU_FreeItemData( item );
4075         }
4076         HeapFree( GetProcessHeap(), 0, lppop->items );
4077     }
4078     HeapFree( GetProcessHeap(), 0, lppop );
4079     return TRUE;
4080 }
4081
4082
4083 /**********************************************************************
4084  *         GetSystemMenu    (USER32.@)
4085  */
4086 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4087 {
4088     WND *wndPtr = WIN_GetPtr( hWnd );
4089     HMENU retvalue = 0;
4090
4091     if (wndPtr == WND_DESKTOP) return 0;
4092     if (wndPtr == WND_OTHER_PROCESS)
4093     {
4094         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4095     }
4096     else if (wndPtr)
4097     {
4098         if (wndPtr->hSysMenu && bRevert)
4099         {
4100             DestroyMenu(wndPtr->hSysMenu);
4101             wndPtr->hSysMenu = 0;
4102         }
4103
4104         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4105             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4106
4107         if( wndPtr->hSysMenu )
4108         {
4109             POPUPMENU *menu;
4110             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4111
4112             /* Store the dummy sysmenu handle to facilitate the refresh */
4113             /* of the close button if the SC_CLOSE item change */
4114             menu = MENU_GetMenu(retvalue);
4115             if ( menu )
4116                menu->hSysMenuOwner = wndPtr->hSysMenu;
4117         }
4118         WIN_ReleasePtr( wndPtr );
4119     }
4120     return bRevert ? 0 : retvalue;
4121 }
4122
4123
4124 /*******************************************************************
4125  *         SetSystemMenu    (USER32.@)
4126  */
4127 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4128 {
4129     WND *wndPtr = WIN_GetPtr( hwnd );
4130
4131     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4132     {
4133         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4134         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4135         WIN_ReleasePtr( wndPtr );
4136         return TRUE;
4137     }
4138     return FALSE;
4139 }
4140
4141
4142 /**********************************************************************
4143  *         GetMenu    (USER32.@)
4144  */
4145 HMENU WINAPI GetMenu( HWND hWnd )
4146 {
4147     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4148     TRACE("for %p returning %p\n", hWnd, retvalue);
4149     return retvalue;
4150 }
4151
4152 /**********************************************************************
4153  *         GetMenuBarInfo    (USER32.@)
4154  */
4155 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4156 {
4157     FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4158     return FALSE;
4159 }
4160
4161 /**********************************************************************
4162  *         MENU_SetMenu
4163  *
4164  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4165  * SetWindowPos call that would result if SetMenu were called directly.
4166  */
4167 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4168 {
4169     TRACE("(%p, %p);\n", hWnd, hMenu);
4170
4171     if (hMenu && !IsMenu(hMenu))
4172     {
4173         WARN("hMenu %p is not a menu handle\n", hMenu);
4174         return FALSE;
4175     }
4176     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4177         return FALSE;
4178
4179     hWnd = WIN_GetFullHandle( hWnd );
4180     if (GetCapture() == hWnd)
4181         set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
4182
4183     if (hMenu != 0)
4184     {
4185         LPPOPUPMENU lpmenu;
4186
4187         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4188
4189         lpmenu->hWnd = hWnd;
4190         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4191     }
4192     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4193     return TRUE;
4194 }
4195
4196
4197 /**********************************************************************
4198  *         SetMenu    (USER32.@)
4199  */
4200 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4201 {   
4202     if(!MENU_SetMenu(hWnd, hMenu))
4203         return FALSE;
4204  
4205     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4206                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4207     return TRUE;
4208 }
4209
4210
4211 /**********************************************************************
4212  *         GetSubMenu    (USER32.@)
4213  */
4214 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4215 {
4216     MENUITEM * lpmi;
4217
4218     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4219     if (!(lpmi->fType & MF_POPUP)) return 0;
4220     return lpmi->hSubMenu;
4221 }
4222
4223
4224 /**********************************************************************
4225  *         DrawMenuBar    (USER32.@)
4226  */
4227 BOOL WINAPI DrawMenuBar( HWND hWnd )
4228 {
4229     LPPOPUPMENU lppop;
4230     HMENU hMenu = GetMenu(hWnd);
4231
4232     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4233         return FALSE;
4234     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4235
4236     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4237     lppop->hwndOwner = hWnd;
4238     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4239                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4240     return TRUE;
4241 }
4242
4243 /***********************************************************************
4244  *           DrawMenuBarTemp   (USER32.@)
4245  *
4246  * UNDOCUMENTED !!
4247  *
4248  * called by W98SE desk.cpl Control Panel Applet
4249  *
4250  * Not 100% sure about the param names, but close.
4251  */
4252 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4253 {
4254     LPPOPUPMENU lppop;
4255     UINT i,retvalue;
4256     HFONT hfontOld = 0;
4257     BOOL flat_menu = FALSE;
4258
4259     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4260
4261     if (!hMenu)
4262         hMenu = GetMenu(hwnd);
4263
4264     if (!hFont)
4265         hFont = get_menu_font(FALSE);
4266
4267     lppop = MENU_GetMenu( hMenu );
4268     if (lppop == NULL || lprect == NULL)
4269     {
4270         retvalue = GetSystemMetrics(SM_CYMENU);
4271         goto END;
4272     }
4273
4274     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4275
4276     hfontOld = SelectObject( hDC, hFont);
4277
4278     if (lppop->Height == 0)
4279         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4280
4281     lprect->bottom = lprect->top + lppop->Height;
4282
4283     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4284
4285     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4286     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4287     LineTo( hDC, lprect->right, lprect->bottom );
4288
4289     if (lppop->nItems == 0)
4290     {
4291         retvalue = GetSystemMetrics(SM_CYMENU);
4292         goto END;
4293     }
4294
4295     for (i = 0; i < lppop->nItems; i++)
4296     {
4297         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4298                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4299     }
4300     retvalue = lppop->Height;
4301
4302 END:
4303     if (hfontOld) SelectObject (hDC, hfontOld);
4304     return retvalue;
4305 }
4306
4307 /***********************************************************************
4308  *           EndMenu   (USER.187)
4309  *           EndMenu   (USER32.@)
4310  */
4311 BOOL WINAPI EndMenu(void)
4312 {
4313     /* if we are in the menu code, and it is active */
4314     if (!fEndMenu && top_popup)
4315     {
4316         /* terminate the menu handling code */
4317         fEndMenu = TRUE;
4318
4319         /* needs to be posted to wakeup the internal menu handler */
4320         /* which will now terminate the menu, in the event that */
4321         /* the main window was minimized, or lost focus, so we */
4322         /* don't end up with an orphaned menu */
4323         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4324     }
4325     return fEndMenu;
4326 }
4327
4328
4329 /***********************************************************************
4330  *           LookupMenuHandle   (USER.217)
4331  */
4332 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4333 {
4334     HMENU hmenu32 = HMENU_32(hmenu);
4335     UINT id32 = id;
4336     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4337     else return HMENU_16(hmenu32);
4338 }
4339
4340
4341 /**********************************************************************
4342  *          LoadMenu    (USER.150)
4343  */
4344 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4345 {
4346     HRSRC16 hRsrc;
4347     HGLOBAL16 handle;
4348     HMENU16 hMenu;
4349
4350     if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4351     if (!name) return 0;
4352
4353     instance = GetExePtr( instance );
4354     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4355     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4356     hMenu = LoadMenuIndirect16(LockResource16(handle));
4357     FreeResource16( handle );
4358     return hMenu;
4359 }
4360
4361
4362 /*****************************************************************
4363  *        LoadMenuA   (USER32.@)
4364  */
4365 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4366 {
4367     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4368     if (!hrsrc) return 0;
4369     return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4370 }
4371
4372
4373 /*****************************************************************
4374  *        LoadMenuW   (USER32.@)
4375  */
4376 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4377 {
4378     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4379     if (!hrsrc) return 0;
4380     return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4381 }
4382
4383
4384 /**********************************************************************
4385  *          LoadMenuIndirect    (USER.220)
4386  */
4387 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4388 {
4389     HMENU hMenu;
4390     WORD version, offset;
4391     LPCSTR p = template;
4392
4393     TRACE("(%p)\n", template );
4394     version = GET_WORD(p);
4395     p += sizeof(WORD);
4396     if (version)
4397     {
4398         WARN("version must be 0 for Win16\n" );
4399         return 0;
4400     }
4401     offset = GET_WORD(p);
4402     p += sizeof(WORD) + offset;
4403     if (!(hMenu = CreateMenu())) return 0;
4404     if (!MENU_ParseResource( p, hMenu, FALSE ))
4405     {
4406         DestroyMenu( hMenu );
4407         return 0;
4408     }
4409     return HMENU_16(hMenu);
4410 }
4411
4412
4413 /**********************************************************************
4414  *          LoadMenuIndirectW    (USER32.@)
4415  */
4416 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4417 {
4418     HMENU hMenu;
4419     WORD version, offset;
4420     LPCSTR p = template;
4421
4422     version = GET_WORD(p);
4423     p += sizeof(WORD);
4424     TRACE("%p, ver %d\n", template, version );
4425     switch (version)
4426     {
4427       case 0: /* standard format is version of 0 */
4428         offset = GET_WORD(p);
4429         p += sizeof(WORD) + offset;
4430         if (!(hMenu = CreateMenu())) return 0;
4431         if (!MENU_ParseResource( p, hMenu, TRUE ))
4432           {
4433             DestroyMenu( hMenu );
4434             return 0;
4435           }
4436         return hMenu;
4437       case 1: /* extended format is version of 1 */
4438         offset = GET_WORD(p);
4439         p += sizeof(WORD) + offset;
4440         if (!(hMenu = CreateMenu())) return 0;
4441         if (!MENUEX_ParseResource( p, hMenu))
4442           {
4443             DestroyMenu( hMenu );
4444             return 0;
4445           }
4446         return hMenu;
4447       default:
4448         ERR("version %d not supported.\n", version);
4449         return 0;
4450     }
4451 }
4452
4453
4454 /**********************************************************************
4455  *          LoadMenuIndirectA    (USER32.@)
4456  */
4457 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4458 {
4459     return LoadMenuIndirectW( template );
4460 }
4461
4462
4463 /**********************************************************************
4464  *              IsMenu    (USER32.@)
4465  */
4466 BOOL WINAPI IsMenu(HMENU hmenu)
4467 {
4468     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4469
4470     if (!menu)
4471     {
4472         SetLastError(ERROR_INVALID_MENU_HANDLE);
4473         return FALSE;
4474     }
4475     return TRUE;
4476 }
4477
4478 /**********************************************************************
4479  *              GetMenuItemInfo_common
4480  */
4481
4482 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4483                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4484 {
4485     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4486
4487     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4488
4489     if (!menu) {
4490         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4491         return FALSE;
4492     }
4493     
4494     if( lpmii->fMask & MIIM_TYPE) {
4495         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4496             WARN("invalid combination of fMask bits used\n");
4497             /* this does not happen on Win9x/ME */
4498             SetLastError( ERROR_INVALID_PARAMETER);
4499             return FALSE;
4500         }
4501         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4502         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4503         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4504         if( lpmii->fType & MFT_BITMAP) {
4505             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4506             lpmii->cch = 0;
4507         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4508             /* this does not happen on Win9x/ME */
4509             lpmii->dwTypeData = 0;
4510             lpmii->cch = 0;
4511         }
4512     }
4513
4514     /* copy the text string */
4515     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4516          if( !menu->text ) {
4517                 if(lpmii->dwTypeData && lpmii->cch) {
4518                     lpmii->cch = 0;
4519                     if( unicode)
4520                         *((WCHAR *)lpmii->dwTypeData) = 0;
4521                     else
4522                         *((CHAR *)lpmii->dwTypeData) = 0;
4523                 }
4524          } else {
4525             int len;
4526             if (unicode)
4527             {
4528                 len = strlenW(menu->text);
4529                 if(lpmii->dwTypeData && lpmii->cch)
4530                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4531             }
4532             else
4533             {
4534                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4535                         0, NULL, NULL ) - 1;
4536                 if(lpmii->dwTypeData && lpmii->cch)
4537                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4538                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4539                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4540             }
4541             /* if we've copied a substring we return its length */
4542             if(lpmii->dwTypeData && lpmii->cch)
4543                 if (lpmii->cch <= len + 1)
4544                     lpmii->cch--;
4545                 else
4546                     lpmii->cch = len;
4547             else {
4548                 /* return length of string */
4549                 /* not on Win9x/ME if fType & MFT_BITMAP */
4550                 lpmii->cch = len;
4551             }
4552         }
4553     }
4554
4555     if (lpmii->fMask & MIIM_FTYPE)
4556         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4557
4558     if (lpmii->fMask & MIIM_BITMAP)
4559         lpmii->hbmpItem = menu->hbmpItem;
4560
4561     if (lpmii->fMask & MIIM_STATE)
4562         lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4563
4564     if (lpmii->fMask & MIIM_ID)
4565         lpmii->wID = menu->wID;
4566
4567     if (lpmii->fMask & MIIM_SUBMENU)
4568         lpmii->hSubMenu = menu->hSubMenu;
4569     else {
4570         /* hSubMenu is always cleared 
4571          * (not on Win9x/ME ) */
4572         lpmii->hSubMenu = 0;
4573     }
4574
4575     if (lpmii->fMask & MIIM_CHECKMARKS) {
4576         lpmii->hbmpChecked = menu->hCheckBit;
4577         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4578     }
4579     if (lpmii->fMask & MIIM_DATA)
4580         lpmii->dwItemData = menu->dwItemData;
4581
4582   return TRUE;
4583 }
4584
4585 /**********************************************************************
4586  *              GetMenuItemInfoA    (USER32.@)
4587  */
4588 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4589                                   LPMENUITEMINFOA lpmii)
4590 {
4591     BOOL ret;
4592     MENUITEMINFOA mii;
4593     if( lpmii->cbSize != sizeof( mii) &&
4594             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4595         SetLastError( ERROR_INVALID_PARAMETER);
4596         return FALSE;
4597     }
4598     memcpy( &mii, lpmii, lpmii->cbSize);
4599     mii.cbSize = sizeof( mii);
4600     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4601                                     (LPMENUITEMINFOW)&mii, FALSE);
4602     mii.cbSize = lpmii->cbSize;
4603     memcpy( lpmii, &mii, mii.cbSize);
4604     return ret;
4605 }
4606
4607 /**********************************************************************
4608  *              GetMenuItemInfoW    (USER32.@)
4609  */
4610 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4611                                   LPMENUITEMINFOW lpmii)
4612 {
4613     BOOL ret;
4614     MENUITEMINFOW mii;
4615     if( lpmii->cbSize != sizeof( mii) &&
4616             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4617         SetLastError( ERROR_INVALID_PARAMETER);
4618         return FALSE;
4619     }
4620     memcpy( &mii, lpmii, lpmii->cbSize);
4621     mii.cbSize = sizeof( mii);
4622     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4623     mii.cbSize = lpmii->cbSize;
4624     memcpy( lpmii, &mii, mii.cbSize);
4625     return ret;
4626 }
4627
4628
4629 /* set a menu item text from a ASCII or Unicode string */
4630 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4631 {
4632     if (!text)
4633         menu->text = NULL;
4634     else if (unicode)
4635     {
4636         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4637             strcpyW( menu->text, text );
4638     }
4639     else
4640     {
4641         LPCSTR str = (LPCSTR)text;
4642         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4643         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4644             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4645     }
4646 }
4647
4648
4649 /**********************************************************************
4650  *              MENU_depth
4651  *
4652  * detect if there are loops in the menu tree (or the depth is too large)
4653  */
4654 static int MENU_depth( POPUPMENU *pmenu, int depth)
4655 {
4656     int i;
4657     MENUITEM *item;
4658     int subdepth;
4659
4660     depth++;
4661     if( depth > MAXMENUDEPTH) return depth;
4662     item = pmenu->items;
4663     subdepth = depth;
4664     for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4665         POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4666         if( psubmenu){
4667             int bdepth = MENU_depth( psubmenu, depth);
4668             if( bdepth > subdepth) subdepth = bdepth;
4669         }
4670         if( subdepth > MAXMENUDEPTH)
4671             TRACE("<- hmenu %p\n", item->hSubMenu);
4672     }
4673     return subdepth;
4674 }
4675
4676
4677 /**********************************************************************
4678  *              SetMenuItemInfo_common
4679  *
4680  * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4681  * MIIM_BITMAP and MIIM_STRING flags instead.
4682  */
4683
4684 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4685                                        const MENUITEMINFOW *lpmii,
4686                                        BOOL unicode)
4687 {
4688     if (!menu) return FALSE;
4689
4690     debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4691
4692     if (lpmii->fMask & MIIM_FTYPE ) {
4693         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4694         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4695     }
4696     if (lpmii->fMask & MIIM_STRING ) {
4697         /* free the string when used */
4698         HeapFree(GetProcessHeap(), 0, menu->text);
4699         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4700     }
4701
4702     if (lpmii->fMask & MIIM_STATE)
4703          /* Other menu items having MFS_DEFAULT are not converted
4704            to normal items */
4705          menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4706
4707     if (lpmii->fMask & MIIM_ID)
4708         menu->wID = lpmii->wID;
4709
4710     if (lpmii->fMask & MIIM_SUBMENU) {
4711         menu->hSubMenu = lpmii->hSubMenu;
4712         if (menu->hSubMenu) {
4713             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4714             if (subMenu) {
4715                 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4716                     ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4717                     menu->hSubMenu = 0;
4718                     return FALSE;
4719                 }
4720                 subMenu->wFlags |= MF_POPUP;
4721                 menu->fType |= MF_POPUP;
4722             } else {
4723                 SetLastError( ERROR_INVALID_PARAMETER);
4724                 return FALSE;
4725             }
4726         }
4727         else
4728             menu->fType &= ~MF_POPUP;
4729     }
4730
4731     if (lpmii->fMask & MIIM_CHECKMARKS)
4732     {
4733         menu->hCheckBit = lpmii->hbmpChecked;
4734         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4735     }
4736     if (lpmii->fMask & MIIM_DATA)
4737         menu->dwItemData = lpmii->dwItemData;
4738
4739     if (lpmii->fMask & MIIM_BITMAP)
4740         menu->hbmpItem = lpmii->hbmpItem;
4741
4742     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4743         menu->fType |= MFT_SEPARATOR;
4744
4745     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4746     return TRUE;
4747 }
4748
4749 /**********************************************************************
4750  *              MENU_NormalizeMenuItemInfoStruct
4751  *
4752  * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4753  * check, copy and extend the MENUITEMINFO struct from the version that the application
4754  * supplied to the version used by wine source. */
4755 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4756                                               MENUITEMINFOW *pmii_out )
4757 {
4758     /* do we recognize the size? */
4759     if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4760             pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4761         SetLastError( ERROR_INVALID_PARAMETER);
4762         return FALSE;
4763     }
4764     /* copy the fields that we have */
4765     memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4766     /* if the hbmpItem member is missing then extend */
4767     if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4768         pmii_out->cbSize = sizeof( MENUITEMINFOW);
4769         pmii_out->hbmpItem = NULL;
4770     }
4771     /* test for invalid bit combinations */
4772     if( (pmii_out->fMask & MIIM_TYPE &&
4773          pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4774         (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4775         WARN("invalid combination of fMask bits used\n");
4776         /* this does not happen on Win9x/ME */
4777         SetLastError( ERROR_INVALID_PARAMETER);
4778         return FALSE;
4779     }
4780     /* convert old style (MIIM_TYPE) to the new */
4781     if( pmii_out->fMask & MIIM_TYPE){
4782         pmii_out->fMask |= MIIM_FTYPE;
4783         if( IS_STRING_ITEM(pmii_out->fType)){
4784             pmii_out->fMask |= MIIM_STRING;
4785         } else if( (pmii_out->fType) & MFT_BITMAP){
4786             pmii_out->fMask |= MIIM_BITMAP;
4787             pmii_out->hbmpItem = HBITMAP_32(LOWORD(pmii_out->dwTypeData));
4788         }
4789     }
4790     return TRUE;
4791 }
4792
4793 /**********************************************************************
4794  *              SetMenuItemInfoA    (USER32.@)
4795  */
4796 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4797                                  const MENUITEMINFOA *lpmii)
4798 {
4799     MENUITEMINFOW mii;
4800
4801     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4802
4803     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4804
4805     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4806                                   &mii, FALSE);
4807 }
4808
4809 /**********************************************************************
4810  *              SetMenuItemInfoW    (USER32.@)
4811  */
4812 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4813                                  const MENUITEMINFOW *lpmii)
4814 {
4815     MENUITEMINFOW mii;
4816
4817     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4818
4819     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4820     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4821                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4822 }
4823
4824 /**********************************************************************
4825  *              SetMenuDefaultItem    (USER32.@)
4826  *
4827  */
4828 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4829 {
4830         UINT i;
4831         POPUPMENU *menu;
4832         MENUITEM *item;
4833
4834         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4835
4836         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4837
4838         /* reset all default-item flags */
4839         item = menu->items;
4840         for (i = 0; i < menu->nItems; i++, item++)
4841         {
4842             item->fState &= ~MFS_DEFAULT;
4843         }
4844
4845         /* no default item */
4846         if ( -1 == uItem)
4847         {
4848             return TRUE;
4849         }
4850
4851         item = menu->items;
4852         if ( bypos )
4853         {
4854             if ( uItem >= menu->nItems ) return FALSE;
4855             item[uItem].fState |= MFS_DEFAULT;
4856             return TRUE;
4857         }
4858         else
4859         {
4860             for (i = 0; i < menu->nItems; i++, item++)
4861             {
4862                 if (item->wID == uItem)
4863                 {
4864                      item->fState |= MFS_DEFAULT;
4865                      return TRUE;
4866                 }
4867             }
4868
4869         }
4870         return FALSE;
4871 }
4872
4873 /**********************************************************************
4874  *              GetMenuDefaultItem    (USER32.@)
4875  */
4876 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4877 {
4878         POPUPMENU *menu;
4879         MENUITEM * item;
4880         UINT i = 0;
4881
4882         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4883
4884         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4885
4886         /* find default item */
4887         item = menu->items;
4888
4889         /* empty menu */
4890         if (! item) return -1;
4891
4892         while ( !( item->fState & MFS_DEFAULT ) )
4893         {
4894             i++; item++;
4895             if  (i >= menu->nItems ) return -1;
4896         }
4897
4898         /* default: don't return disabled items */
4899         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4900
4901         /* search rekursiv when needed */
4902         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4903         {
4904             UINT ret;
4905             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4906             if ( -1 != ret ) return ret;
4907
4908             /* when item not found in submenu, return the popup item */
4909         }
4910         return ( bypos ) ? i : item->wID;
4911
4912 }
4913
4914
4915 /**********************************************************************
4916  *              InsertMenuItemA    (USER32.@)
4917  */
4918 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4919                                 const MENUITEMINFOA *lpmii)
4920 {
4921     MENUITEM *item;
4922     MENUITEMINFOW mii;
4923
4924     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4925
4926     if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4927
4928     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4929     return SetMenuItemInfo_common(item, &mii, FALSE);
4930 }
4931
4932
4933 /**********************************************************************
4934  *              InsertMenuItemW    (USER32.@)
4935  */
4936 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4937                                 const MENUITEMINFOW *lpmii)
4938 {
4939     MENUITEM *item;
4940     MENUITEMINFOW mii;
4941
4942     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4943
4944     if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4945
4946     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4947     return SetMenuItemInfo_common(item, &mii, TRUE);
4948 }
4949
4950 /**********************************************************************
4951  *              CheckMenuRadioItem    (USER32.@)
4952  */
4953
4954 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4955                                    UINT first, UINT last, UINT check,
4956                                    UINT bypos)
4957 {
4958     BOOL done = FALSE;
4959     UINT i;
4960     MENUITEM *mi_first = NULL, *mi_check;
4961     HMENU m_first, m_check;
4962
4963     for (i = first; i <= last; i++)
4964     {
4965         UINT pos = i;
4966
4967         if (!mi_first)
4968         {
4969             m_first = hMenu;
4970             mi_first = MENU_FindItem(&m_first, &pos, bypos);
4971             if (!mi_first) continue;
4972             mi_check = mi_first;
4973             m_check = m_first;
4974         }
4975         else
4976         {
4977             m_check = hMenu;
4978             mi_check = MENU_FindItem(&m_check, &pos, bypos);
4979             if (!mi_check) continue;
4980         }
4981
4982         if (m_first != m_check) continue;
4983         if (mi_check->fType == MFT_SEPARATOR) continue;
4984
4985         if (i == check)
4986         {
4987             mi_check->fType |= MFT_RADIOCHECK;
4988             mi_check->fState |= MFS_CHECKED;
4989             done = TRUE;
4990         }
4991         else
4992         {
4993             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4994             mi_check->fState &= ~MFS_CHECKED;
4995         }
4996     }
4997
4998     return done;
4999 }
5000
5001
5002 /**********************************************************************
5003  *              GetMenuItemRect    (USER32.@)
5004  *
5005  *      ATTENTION: Here, the returned values in rect are the screen
5006  *                 coordinates of the item just like if the menu was
5007  *                 always on the upper left side of the application.
5008  *
5009  */
5010 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
5011                                  LPRECT rect)
5012 {
5013      POPUPMENU *itemMenu;
5014      MENUITEM *item;
5015      HWND referenceHwnd;
5016
5017      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
5018
5019      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5020      referenceHwnd = hwnd;
5021
5022      if(!hwnd)
5023      {
5024          itemMenu = MENU_GetMenu(hMenu);
5025          if (itemMenu == NULL)
5026              return FALSE;
5027
5028          if(itemMenu->hWnd == 0)
5029              return FALSE;
5030          referenceHwnd = itemMenu->hWnd;
5031      }
5032
5033      if ((rect == NULL) || (item == NULL))
5034          return FALSE;
5035
5036      *rect = item->rect;
5037
5038      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5039
5040      return TRUE;
5041 }
5042
5043 /**********************************************************************
5044  *              SetMenuInfo    (USER32.@)
5045  *
5046  * FIXME
5047  *      actually use the items to draw the menu
5048  *      (recalculate and/or redraw)
5049  */
5050 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5051 {
5052     POPUPMENU *menu;
5053     if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5054
5055     if (lpmi->fMask & MIM_BACKGROUND)
5056         menu->hbrBack = lpmi->hbrBack;
5057
5058     if (lpmi->fMask & MIM_HELPID)
5059         menu->dwContextHelpID = lpmi->dwContextHelpID;
5060
5061     if (lpmi->fMask & MIM_MAXHEIGHT)
5062         menu->cyMax = lpmi->cyMax;
5063
5064     if (lpmi->fMask & MIM_MENUDATA)
5065         menu->dwMenuData = lpmi->dwMenuData;
5066
5067     if (lpmi->fMask & MIM_STYLE)
5068         menu->dwStyle = lpmi->dwStyle;
5069
5070     if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5071         int i;
5072         MENUITEM *item = menu->items;
5073         for( i = menu->nItems; i; i--, item++)
5074             if( item->fType & MF_POPUP)
5075                 menu_SetMenuInfo( item->hSubMenu, lpmi);
5076     }
5077     return TRUE;
5078 }
5079
5080 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5081 {
5082     TRACE("(%p %p)\n", hMenu, lpmi);
5083     if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5084         if( lpmi->fMask & MIM_STYLE) {
5085             if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5086             if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5087             if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5088         }
5089         return TRUE;
5090     }
5091     SetLastError( ERROR_INVALID_PARAMETER);
5092     return FALSE;
5093 }
5094
5095 /**********************************************************************
5096  *              GetMenuInfo    (USER32.@)
5097  *
5098  *  NOTES
5099  *      win98/NT5.0
5100  *
5101  */
5102 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5103 {   POPUPMENU *menu;
5104
5105     TRACE("(%p %p)\n", hMenu, lpmi);
5106
5107     if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5108     {
5109
5110         if (lpmi->fMask & MIM_BACKGROUND)
5111             lpmi->hbrBack = menu->hbrBack;
5112
5113         if (lpmi->fMask & MIM_HELPID)
5114             lpmi->dwContextHelpID = menu->dwContextHelpID;
5115
5116         if (lpmi->fMask & MIM_MAXHEIGHT)
5117             lpmi->cyMax = menu->cyMax;
5118
5119         if (lpmi->fMask & MIM_MENUDATA)
5120             lpmi->dwMenuData = menu->dwMenuData;
5121
5122         if (lpmi->fMask & MIM_STYLE)
5123             lpmi->dwStyle = menu->dwStyle;
5124
5125         return TRUE;
5126     }
5127     SetLastError( ERROR_INVALID_PARAMETER);
5128     return FALSE;
5129 }
5130
5131
5132 /**********************************************************************
5133  *         SetMenuContextHelpId    (USER32.@)
5134  */
5135 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5136 {
5137     LPPOPUPMENU menu;
5138
5139     TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5140
5141     if ((menu = MENU_GetMenu(hMenu)))
5142     {
5143         menu->dwContextHelpID = dwContextHelpID;
5144         return TRUE;
5145     }
5146     return FALSE;
5147 }
5148
5149
5150 /**********************************************************************
5151  *         GetMenuContextHelpId    (USER32.@)
5152  */
5153 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5154 {
5155     LPPOPUPMENU menu;
5156
5157     TRACE("(%p)\n", hMenu);
5158
5159     if ((menu = MENU_GetMenu(hMenu)))
5160     {
5161         return menu->dwContextHelpID;
5162     }
5163     return 0;
5164 }
5165
5166 /**********************************************************************
5167  *         MenuItemFromPoint    (USER32.@)
5168  */
5169 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5170 {
5171     POPUPMENU *menu = MENU_GetMenu(hMenu);
5172     UINT pos;
5173
5174     /*FIXME: Do we have to handle hWnd here? */
5175     if (!menu) return -1;
5176     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5177     return pos;
5178 }
5179
5180
5181 /**********************************************************************
5182  *           translate_accelerator
5183  */
5184 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5185                                    BYTE fVirt, WORD key, WORD cmd )
5186 {
5187     INT mask = 0;
5188     UINT mesg = 0;
5189
5190     if (wParam != key) return FALSE;
5191
5192     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5193     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5194     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5195
5196     if (message == WM_CHAR || message == WM_SYSCHAR)
5197     {
5198         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5199         {
5200             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5201             goto found;
5202         }
5203     }
5204     else
5205     {
5206         if(fVirt & FVIRTKEY)
5207         {
5208             TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5209                           wParam, 0xff & HIWORD(lParam));
5210
5211             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5212             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5213         }
5214         else
5215         {
5216             if (!(lParam & 0x01000000))  /* no special_key */
5217             {
5218                 if ((fVirt & FALT) && (lParam & 0x20000000))
5219                 {                              /* ^^ ALT pressed */
5220                     TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5221                     goto found;
5222                 }
5223             }
5224         }
5225     }
5226     return FALSE;
5227
5228  found:
5229     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5230         mesg = 1;
5231     else
5232     {
5233         HMENU hMenu, hSubMenu, hSysMenu;
5234         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5235
5236         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5237         hSysMenu = get_win_sys_menu( hWnd );
5238
5239         /* find menu item and ask application to initialize it */
5240         /* 1. in the system menu */
5241         hSubMenu = hSysMenu;
5242         nPos = cmd;
5243         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5244         {
5245             if (GetCapture())
5246                 mesg = 2;
5247             if (!IsWindowEnabled(hWnd))
5248                 mesg = 3;
5249             else
5250             {
5251                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5252                 if(hSubMenu != hSysMenu)
5253                 {
5254                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5255                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5256                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5257                 }
5258                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5259             }
5260         }
5261         else /* 2. in the window's menu */
5262         {
5263             hSubMenu = hMenu;
5264             nPos = cmd;
5265             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5266             {
5267                 if (GetCapture())
5268                     mesg = 2;
5269                 if (!IsWindowEnabled(hWnd))
5270                     mesg = 3;
5271                 else
5272                 {
5273                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5274                     if(hSubMenu != hMenu)
5275                     {
5276                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5277                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5278                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5279                     }
5280                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5281                 }
5282             }
5283         }
5284
5285         if (mesg == 0)
5286         {
5287             if (uSysStat != (UINT)-1)
5288             {
5289                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5290                     mesg=4;
5291                 else
5292                     mesg=WM_SYSCOMMAND;
5293             }
5294             else
5295             {
5296                 if (uStat != (UINT)-1)
5297                 {
5298                     if (IsIconic(hWnd))
5299                         mesg=5;
5300                     else
5301                     {
5302                         if (uStat & (MF_DISABLED|MF_GRAYED))
5303                             mesg=6;
5304                         else
5305                             mesg=WM_COMMAND;
5306                     }
5307                 }
5308                 else
5309                     mesg=WM_COMMAND;
5310             }
5311         }
5312     }
5313
5314     if( mesg==WM_COMMAND )
5315     {
5316         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5317         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5318     }
5319     else if( mesg==WM_SYSCOMMAND )
5320     {
5321         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5322         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5323     }
5324     else
5325     {
5326         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5327          *   #0: unknown (please report!)
5328          *   #1: for WM_KEYUP,WM_SYSKEYUP
5329          *   #2: mouse is captured
5330          *   #3: window is disabled
5331          *   #4: it's a disabled system menu option
5332          *   #5: it's a menu option, but window is iconic
5333          *   #6: it's a menu option, but disabled
5334          */
5335         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5336         if(mesg==0)
5337             ERR_(accel)(" unknown reason - please report!\n");
5338     }
5339     return TRUE;
5340 }
5341
5342 /**********************************************************************
5343  *      TranslateAcceleratorA     (USER32.@)
5344  *      TranslateAccelerator      (USER32.@)
5345  */
5346 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5347 {
5348     switch (msg->message)
5349     {
5350     case WM_KEYDOWN:
5351     case WM_SYSKEYDOWN:
5352         return TranslateAcceleratorW( hWnd, hAccel, msg );
5353
5354     case WM_CHAR:
5355     case WM_SYSCHAR:
5356         {
5357             MSG msgW = *msg;
5358             char ch = LOWORD(msg->wParam);
5359             WCHAR wch;
5360             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5361             msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5362             return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5363         }
5364
5365     default:
5366         return 0;
5367     }
5368 }
5369
5370 /**********************************************************************
5371  *      TranslateAcceleratorW     (USER32.@)
5372  */
5373 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5374 {
5375     ACCEL data[32], *ptr = data;
5376     int i, count;
5377
5378     if (!hWnd) return 0;
5379
5380     if (msg->message != WM_KEYDOWN &&
5381         msg->message != WM_SYSKEYDOWN &&
5382         msg->message != WM_CHAR &&
5383         msg->message != WM_SYSCHAR)
5384         return 0;
5385
5386     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5387                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5388
5389     if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5390     if (count > sizeof(data)/sizeof(data[0]))
5391     {
5392         if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5393     }
5394     count = CopyAcceleratorTableW( hAccel, ptr, count );
5395     for (i = 0; i < count; i++)
5396     {
5397         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5398                                    ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5399             break;
5400     }
5401     if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5402     return (i < count);
5403 }