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