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