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