user32: Simplify some code.
[wine] / dlls / user32 / menu.c
1 /*
2  * Menu functions
3  *
4  * Copyright 1993 Martin Ayotte
5  * Copyright 1994 Alexandre Julliard
6  * Copyright 1997 Morten Welinder
7  * Copyright 2005 Maxime Bellengé
8  * Copyright 2006 Phil Krylov
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 /*
26  * Note: the style MF_MOUSESELECT is used to mark popup items that
27  * have been selected, i.e. their popup menu is currently displayed.
28  * This is probably not the meaning this style has in MS-Windows.
29  *
30  * Note 2: where there is a difference, these menu API's are according
31  * the behavior of Windows 2k and Windows XP. Known differences with
32  * Windows 9x/ME are documented in the comments, in case an application
33  * is found to depend on the old behavior.
34  * 
35  * TODO:
36  *    implements styles :
37  *        - MNS_AUTODISMISS
38  *        - MNS_DRAGDROP
39  *        - MNS_MODELESS
40  */
41
42 #include "config.h"
43 #include "wine/port.h"
44
45 #include <stdarg.h>
46 #include <string.h>
47
48 #define OEMRESOURCE
49
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
56 #include "wownt32.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "win.h"
60 #include "controls.h"
61 #include "user_private.h"
62 #include "wine/debug.h"
63
64 WINE_DEFAULT_DEBUG_CHANNEL(menu);
65 WINE_DECLARE_DEBUG_CHANNEL(accel);
66
67 /* internal popup menu window messages */
68
69 #define MM_SETMENUHANDLE        (WM_USER + 0)
70 #define MM_GETMENUHANDLE        (WM_USER + 1)
71
72 /* Menu item structure */
73 typedef struct {
74     /* ----------- MENUITEMINFO Stuff ----------- */
75     UINT fType;                 /* Item type. */
76     UINT fState;                /* Item state.  */
77     UINT_PTR wID;               /* Item id.  */
78     HMENU hSubMenu;             /* Pop-up menu.  */
79     HBITMAP hCheckBit;          /* Bitmap when checked.  */
80     HBITMAP hUnCheckBit;        /* Bitmap when unchecked.  */
81     LPWSTR text;                /* Item text. */
82     ULONG_PTR dwItemData;       /* Application defined.  */
83     LPWSTR dwTypeData;          /* depends on fMask */
84     HBITMAP hbmpItem;           /* bitmap */
85     /* ----------- Wine stuff ----------- */
86     RECT      rect;             /* Item area (relative to menu window) */
87     UINT      xTab;             /* X position of text after Tab */
88     SIZE   bmpsize;             /* size needed for the HBMMENU_CALLBACK
89                                  * bitmap */ 
90 } MENUITEM;
91
92 /* Popup menu structure */
93 typedef struct {
94     WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
95     WORD        wMagic;       /* Magic number */
96     WORD        Width;        /* Width of the whole menu */
97     WORD        Height;       /* Height of the whole menu */
98     UINT        nItems;       /* Number of items in the menu */
99     HWND        hWnd;         /* Window containing the menu */
100     MENUITEM    *items;       /* Array of menu items */
101     UINT        FocusedItem;  /* Currently focused item */
102     HWND        hwndOwner;    /* window receiving the messages for ownerdraw */
103     BOOL        bTimeToHide;  /* Request hiding when receiving a second click in the top-level menu item */
104     BOOL        bScrolling;   /* Scroll arrows are active */
105     UINT        nScrollPos;   /* Current scroll position */
106     UINT        nTotalHeight; /* Total height of menu items inside menu */
107     /* ------------ MENUINFO members ------ */
108     DWORD       dwStyle;        /* Extended menu style */
109     UINT        cyMax;          /* max height of the whole menu, 0 is screen height */
110     HBRUSH      hbrBack;        /* brush for menu background */
111     DWORD       dwContextHelpID;
112     DWORD       dwMenuData;     /* application defined value */
113     HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
114     SIZE        maxBmpSize;     /* Maximum size of the bitmap items */
115 } POPUPMENU, *LPPOPUPMENU;
116
117 /* internal flags for menu tracking */
118
119 #define TF_ENDMENU              0x0001
120 #define TF_SUSPENDPOPUP         0x0002
121 #define TF_SKIPREMOVE           0x0004
122
123 typedef struct
124 {
125     UINT        trackFlags;
126     HMENU       hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
127     HMENU       hTopMenu;     /* initial menu */
128     HWND        hOwnerWnd;    /* where notifications are sent */
129     POINT       pt;
130 } MTRACKER;
131
132 #define MENU_MAGIC   0x554d  /* 'MU' */
133
134 #define ITEM_PREV               -1
135 #define ITEM_NEXT                1
136
137   /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL            0xF0000000
139 #define TPM_ENTERIDLEEX         0x80000000              /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN          0x40000000              /* menu was clicked before tracking */
141 #define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
142
143   /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
145
146   /*  top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
149
150   /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM  0xffff
152
153 #define MENU_ITEM_TYPE(flags) \
154   ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159
160 #define IS_SYSTEM_MENU(menu)  \
161         (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162
163 #define MENUITEMINFO_TYPE_MASK \
164                 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165                 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166                 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
170
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
172
173 static SIZE     menucharsize;
174 static UINT     ODitemheight; /* default owner drawn item height */      
175
176 /* Use global popup window because there's no way 2 menus can
177  * be tracked at the same time.  */
178 static HWND top_popup;
179
180   /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu = FALSE;
182
183 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
184
185 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
186
187 /*********************************************************************
188  * menu class descriptor
189  */
190 const struct builtin_class_descr MENU_builtin_class =
191 {
192     (LPCWSTR)POPUPMENU_CLASS_ATOM,  /* name */
193     CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
194     NULL,                          /* procA (winproc is Unicode only) */
195     PopupMenuWndProc,              /* procW */
196     sizeof(HMENU),                 /* extra */
197     IDC_ARROW,                     /* cursor */
198     (HBRUSH)(COLOR_MENU+1)         /* brush */
199 };
200
201
202 /***********************************************************************
203  *           debug_print_menuitem
204  *
205  * Print a menuitem in readable form.
206  */
207
208 #define debug_print_menuitem(pre, mp, post) \
209     do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
210
211 #define MENUOUT(text) \
212   TRACE("%s%s", (count++ ? "," : ""), (text))
213
214 #define MENUFLAG(bit,text) \
215   do { \
216     if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
217   } while (0)
218
219 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
220                                     const char *postfix)
221 {
222     static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223     "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224     "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225     "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226     TRACE("%s ", prefix);
227     if (mp) {
228         UINT flags = mp->fType;
229         TRACE( "{ ID=0x%lx", mp->wID);
230         if ( mp->hSubMenu)
231             TRACE( ", Sub=%p", mp->hSubMenu);
232         if (flags) {
233             int count = 0;
234             TRACE( ", fType=");
235             MENUFLAG( MFT_SEPARATOR, "sep");
236             MENUFLAG( MFT_OWNERDRAW, "own");
237             MENUFLAG( MFT_BITMAP, "bit");
238             MENUFLAG(MF_POPUP, "pop");
239             MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240             MENUFLAG(MFT_MENUBREAK, "brk");
241             MENUFLAG(MFT_RADIOCHECK, "radio");
242             MENUFLAG(MFT_RIGHTORDER, "rorder");
243             MENUFLAG(MF_SYSMENU, "sys");
244             MENUFLAG(MFT_RIGHTJUSTIFY, "right");  /* same as MF_HELP */
245             if (flags)
246                 TRACE( "+0x%x", flags);
247         }
248         flags = mp->fState;
249         if (flags) {
250             int count = 0;
251             TRACE( ", State=");
252             MENUFLAG(MFS_GRAYED, "grey");
253             MENUFLAG(MFS_DEFAULT, "default");
254             MENUFLAG(MFS_DISABLED, "dis");
255             MENUFLAG(MFS_CHECKED, "check");
256             MENUFLAG(MFS_HILITE, "hi");
257             MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258             MENUFLAG(MF_MOUSESELECT, "mouse");
259             if (flags)
260                 TRACE( "+0x%x", flags);
261         }
262         if (mp->hCheckBit)
263             TRACE( ", Chk=%p", mp->hCheckBit);
264         if (mp->hUnCheckBit)
265             TRACE( ", Unc=%p", mp->hUnCheckBit);
266         if (mp->text)
267             TRACE( ", Text=%s", debugstr_w(mp->text));
268         if (mp->dwItemData)
269             TRACE( ", ItemData=0x%08lx", mp->dwItemData);
270         if (mp->hbmpItem)
271         {
272             if( IS_MAGIC_BITMAP(mp->hbmpItem))
273                 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
274             else
275                 TRACE( ", hbitmap=%p", mp->hbmpItem);
276         }
277         TRACE( " }");
278     } else
279         TRACE( "NULL");
280     TRACE(" %s\n", postfix);
281 }
282
283 #undef MENUOUT
284 #undef MENUFLAG
285
286
287 /***********************************************************************
288  *           MENU_GetMenu
289  *
290  * Validate the given menu handle and returns the menu structure pointer.
291  */
292 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
293 {
294     POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
295     if (!menu || menu->wMagic != MENU_MAGIC)
296     {
297         WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
298         menu = NULL;
299     }
300     return menu;
301 }
302
303 /***********************************************************************
304  *           get_win_sys_menu
305  *
306  * Get the system menu of a window
307  */
308 static HMENU get_win_sys_menu( HWND hwnd )
309 {
310     HMENU ret = 0;
311     WND *win = WIN_GetPtr( hwnd );
312     if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
313     {
314         ret = win->hSysMenu;
315         WIN_ReleasePtr( win );
316     }
317     return ret;
318 }
319
320 /***********************************************************************
321  *           get_menu_font
322  */
323 static HFONT get_menu_font( BOOL bold )
324 {
325     static HFONT hMenuFont, hMenuFontBold;
326
327     HFONT ret = bold ? hMenuFontBold : hMenuFont;
328
329     if (!ret)
330     {
331         NONCLIENTMETRICSW ncm;
332         HFONT prev;
333
334         ncm.cbSize = sizeof(NONCLIENTMETRICSW);
335         SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
336
337         if (bold)
338         {
339             ncm.lfMenuFont.lfWeight += 300;
340             if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
341         }
342         if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
343         prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
344                                                   ret, NULL );
345         if (prev)
346         {
347             /* another thread beat us to it */
348             DeleteObject( ret );
349             ret = prev;
350         }
351     }
352     return ret;
353 }
354
355 /***********************************************************************
356  *           get_arrow_bitmap
357  */
358 static HBITMAP get_arrow_bitmap(void)
359 {
360     static HBITMAP arrow_bitmap;
361
362     if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
363     return arrow_bitmap;
364 }
365
366 /***********************************************************************
367  *           get_down_arrow_bitmap
368  */
369 static HBITMAP get_down_arrow_bitmap(void)
370 {
371     static HBITMAP arrow_bitmap;
372
373     if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
374     return arrow_bitmap;
375 }
376
377 /***********************************************************************
378  *           get_down_arrow_inactive_bitmap
379  */
380 static HBITMAP get_down_arrow_inactive_bitmap(void)
381 {
382     static HBITMAP arrow_bitmap;
383
384     if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
385     return arrow_bitmap;
386 }
387
388 /***********************************************************************
389  *           get_up_arrow_bitmap
390  */
391 static HBITMAP get_up_arrow_bitmap(void)
392 {
393     static HBITMAP arrow_bitmap;
394
395     if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
396     return arrow_bitmap;
397 }
398
399 /***********************************************************************
400  *           get_up_arrow_inactive_bitmap
401  */
402 static HBITMAP get_up_arrow_inactive_bitmap(void)
403 {
404     static HBITMAP arrow_bitmap;
405
406     if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
407     return arrow_bitmap;
408 }
409
410 /***********************************************************************
411  *           MENU_CopySysPopup
412  *
413  * Return the default system menu.
414  */
415 static HMENU MENU_CopySysPopup(void)
416 {
417     static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
418     HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
419
420     if( hMenu ) {
421         POPUPMENU* menu = MENU_GetMenu(hMenu);
422         menu->wFlags |= MF_SYSMENU | MF_POPUP;
423         SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
424     }
425     else
426         ERR("Unable to load default system menu\n" );
427
428     TRACE("returning %p.\n", hMenu );
429
430     return hMenu;
431 }
432
433
434 /**********************************************************************
435  *           MENU_GetSysMenu
436  *
437  * Create a copy of the system menu. System menu in Windows is
438  * a special menu bar with the single entry - system menu popup.
439  * This popup is presented to the outside world as a "system menu".
440  * However, the real system menu handle is sometimes seen in the
441  * WM_MENUSELECT parameters (and Word 6 likes it this way).
442  */
443 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
444 {
445     HMENU hMenu;
446
447     TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
448     if ((hMenu = CreateMenu()))
449     {
450         POPUPMENU *menu = MENU_GetMenu(hMenu);
451         menu->wFlags = MF_SYSMENU;
452         menu->hWnd = WIN_GetFullHandle( hWnd );
453         TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
454
455         if (!hPopupMenu)
456             hPopupMenu = MENU_CopySysPopup();
457
458         if (hPopupMenu)
459         {
460             if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
461                 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
462
463             InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
464                          (UINT_PTR)hPopupMenu, NULL );
465
466             menu->items[0].fType = MF_SYSMENU | MF_POPUP;
467             menu->items[0].fState = 0;
468             if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
469
470             TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
471             return hMenu;
472         }
473         DestroyMenu( hMenu );
474     }
475     ERR("failed to load system menu!\n");
476     return 0;
477 }
478
479
480 /***********************************************************************
481  *           MENU_InitSysMenuPopup
482  *
483  * Grey the appropriate items in System menu.
484  */
485 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
486 {
487     BOOL gray;
488
489     gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
490     EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
491     gray = ((style & WS_MAXIMIZE) != 0);
492     EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
493     gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
494     EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495     gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
496     EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
497     gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
498     EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
499     gray = (clsStyle & CS_NOCLOSE) != 0;
500
501     /* The menu item must keep its state if it's disabled */
502     if(gray)
503         EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
504 }
505
506
507 /******************************************************************************
508  *
509  *   UINT  MENU_GetStartOfNextColumn(
510  *     HMENU  hMenu )
511  *
512  *****************************************************************************/
513
514 static UINT  MENU_GetStartOfNextColumn(
515     HMENU  hMenu )
516 {
517     POPUPMENU *menu = MENU_GetMenu(hMenu);
518     UINT i;
519
520     if(!menu)
521         return NO_SELECTED_ITEM;
522
523     i = menu->FocusedItem + 1;
524     if( i == NO_SELECTED_ITEM )
525         return i;
526
527     for( ; i < menu->nItems; ++i ) {
528         if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
529             return i;
530     }
531
532     return NO_SELECTED_ITEM;
533 }
534
535
536 /******************************************************************************
537  *
538  *   UINT  MENU_GetStartOfPrevColumn(
539  *     HMENU  hMenu )
540  *
541  *****************************************************************************/
542
543 static UINT  MENU_GetStartOfPrevColumn(
544     HMENU  hMenu )
545 {
546     POPUPMENU *menu = MENU_GetMenu(hMenu);
547     UINT  i;
548
549     if( !menu )
550         return NO_SELECTED_ITEM;
551
552     if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
553         return NO_SELECTED_ITEM;
554
555     /* Find the start of the column */
556
557     for(i = menu->FocusedItem; i != 0 &&
558          !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
559         --i); /* empty */
560
561     if(i == 0)
562         return NO_SELECTED_ITEM;
563
564     for(--i; i != 0; --i) {
565         if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
566             break;
567     }
568
569     TRACE("ret %d.\n", i );
570
571     return i;
572 }
573
574
575
576 /***********************************************************************
577  *           MENU_FindItem
578  *
579  * Find a menu item. Return a pointer on the item, and modifies *hmenu
580  * in case the item was in a sub-menu.
581  */
582 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
583 {
584     POPUPMENU *menu;
585     MENUITEM *fallback = NULL;
586     UINT fallback_pos = 0;
587     UINT i;
588
589     if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
590     if (wFlags & MF_BYPOSITION)
591     {
592         if (*nPos >= menu->nItems) return NULL;
593         return &menu->items[*nPos];
594     }
595     else
596     {
597         MENUITEM *item = menu->items;
598         for (i = 0; i < menu->nItems; i++, item++)
599         {
600             if (item->fType & MF_POPUP)
601             {
602                 HMENU hsubmenu = item->hSubMenu;
603                 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
604                 if (subitem)
605                 {
606                     *hmenu = hsubmenu;
607                     return subitem;
608                 }
609                 else if (item->wID == *nPos)
610                 {
611                     /* fallback to this item if nothing else found */
612                     fallback_pos = i;
613                     fallback = item;
614                 }
615             }
616             else if (item->wID == *nPos)
617             {
618                 *nPos = i;
619                 return item;
620             }
621         }
622     }
623
624     if (fallback)
625         *nPos = fallback_pos;
626
627     return fallback;
628 }
629
630 /***********************************************************************
631  *           MENU_FindSubMenu
632  *
633  * Find a Sub menu. Return the position of the submenu, and modifies
634  * *hmenu in case it is found in another sub-menu.
635  * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
636  */
637 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
638 {
639     POPUPMENU *menu;
640     UINT i;
641     MENUITEM *item;
642     if (((*hmenu)==(HMENU)0xffff) ||
643             (!(menu = MENU_GetMenu(*hmenu))))
644         return NO_SELECTED_ITEM;
645     item = menu->items;
646     for (i = 0; i < menu->nItems; i++, item++) {
647         if(!(item->fType & MF_POPUP)) continue;
648         if (item->hSubMenu == hSubTarget) {
649             return i;
650         }
651         else  {
652             HMENU hsubmenu = item->hSubMenu;
653             UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
654             if (pos != NO_SELECTED_ITEM) {
655                 *hmenu = hsubmenu;
656                 return pos;
657             }
658         }
659     }
660     return NO_SELECTED_ITEM;
661 }
662
663 /***********************************************************************
664  *           MENU_FreeItemData
665  */
666 static void MENU_FreeItemData( MENUITEM* item )
667 {
668     /* delete text */
669     HeapFree( GetProcessHeap(), 0, item->text );
670 }
671
672 /***********************************************************************
673  *           MENU_AdjustMenuItemRect
674  *
675  * Adjust menu item rectangle according to scrolling state.
676  */
677 static void
678 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
679 {
680     if (menu->bScrolling)
681     {
682         UINT arrow_bitmap_width, arrow_bitmap_height;
683         BITMAP bmp;
684
685         GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
686         arrow_bitmap_width = bmp.bmWidth;
687         arrow_bitmap_height = bmp.bmHeight;
688         rect->top += arrow_bitmap_height - menu->nScrollPos;
689         rect->bottom += arrow_bitmap_height - menu->nScrollPos;
690     }
691 }
692
693
694 /***********************************************************************
695  *           MENU_FindItemByCoords
696  *
697  * Find the item at the specified coordinates (screen coords). Does
698  * not work for child windows and therefore should not be called for
699  * an arbitrary system menu.
700  */
701 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
702                                         POINT pt, UINT *pos )
703 {
704     MENUITEM *item;
705     UINT i;
706     RECT rect;
707
708     if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
709     pt.x -= rect.left;
710     pt.y -= rect.top;
711     item = menu->items;
712     for (i = 0; i < menu->nItems; i++, item++)
713     {
714         rect = item->rect;
715         MENU_AdjustMenuItemRect(menu, &rect);
716         if (PtInRect(&rect, pt))
717         {
718             if (pos) *pos = i;
719             return item;
720         }
721     }
722     return NULL;
723 }
724
725
726 /***********************************************************************
727  *           MENU_FindItemByKey
728  *
729  * Find the menu item selected by a key press.
730  * Return item id, -1 if none, -2 if we should close the menu.
731  */
732 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
733                                 WCHAR key, BOOL forceMenuChar )
734 {
735     TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736
737     if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
738
739     if (hmenu)
740     {
741         POPUPMENU *menu = MENU_GetMenu( hmenu );
742         MENUITEM *item = menu->items;
743         LRESULT menuchar;
744
745         if( !forceMenuChar )
746         {
747              UINT i;
748
749              for (i = 0; i < menu->nItems; i++, item++)
750              {
751                 if( item->text)
752                 {
753                     WCHAR *p = item->text - 2;
754                     do
755                     {
756                         p = strchrW (p + 2, '&');
757                     }
758                     while (p != NULL && p [1] == '&');
759                     if (p && (toupperW(p[1]) == toupperW(key))) return i;
760                 }
761              }
762         }
763         menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
764                                  MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
765         if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
766         if (HIWORD(menuchar) == 1) return (UINT)(-2);
767     }
768     return (UINT)(-1);
769 }
770
771
772 /***********************************************************************
773  *           MENU_GetBitmapItemSize
774  *
775  * Get the size of a bitmap item.
776  */
777 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
778                                     HWND hwndOwner)
779 {
780     BITMAP bm;
781     HBITMAP bmp = lpitem->hbmpItem;
782
783     size->cx = size->cy = 0;
784
785     /* check if there is a magic menu item associated with this item */
786     switch( (INT_PTR) bmp )
787     {
788     case (INT_PTR)HBMMENU_CALLBACK:
789         {
790             MEASUREITEMSTRUCT measItem;
791             measItem.CtlType = ODT_MENU;
792             measItem.CtlID = 0;
793             measItem.itemID = lpitem->wID;
794             measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
795             measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
796             measItem.itemData = lpitem->dwItemData;
797             SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
798             size->cx = measItem.itemWidth;
799             size->cy = measItem.itemHeight;
800             return;
801         }
802         break;
803     case (INT_PTR)HBMMENU_SYSTEM:
804         if (lpitem->dwItemData)
805         {
806             bmp = (HBITMAP)lpitem->dwItemData;
807             break;
808         }
809         /* fall through */
810     case (INT_PTR)HBMMENU_MBAR_RESTORE:
811     case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
812     case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
813     case (INT_PTR)HBMMENU_MBAR_CLOSE:
814     case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
815         size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
816         size->cy = size->cx;
817         return;
818     case (INT_PTR)HBMMENU_POPUP_CLOSE:
819     case (INT_PTR)HBMMENU_POPUP_RESTORE:
820     case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
821     case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
822         FIXME("Magic %p not implemented\n", bmp );
823         return;
824     }
825     if (GetObjectW(bmp, sizeof(bm), &bm ))
826     {
827         size->cx = bm.bmWidth;
828         size->cy = bm.bmHeight;
829     }
830 }
831
832 /***********************************************************************
833  *           MENU_DrawBitmapItem
834  *
835  * Draw a bitmap item.
836  */
837 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
838                     HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
839 {
840     BITMAP bm;
841     DWORD rop;
842     HDC hdcMem;
843     HBITMAP bmp;
844     int w = rect->right - rect->left;
845     int h = rect->bottom - rect->top;
846     int bmp_xoffset = 0;
847     int left, top;
848     HBITMAP hbmToDraw = lpitem->hbmpItem;
849     bmp = hbmToDraw;
850
851     /* Check if there is a magic menu item associated with this item */
852     if (IS_MAGIC_BITMAP(hbmToDraw))
853     {
854         UINT flags = 0;
855         RECT r;
856
857         switch((INT_PTR)hbmToDraw)
858         {
859         case (INT_PTR)HBMMENU_SYSTEM:
860             if (lpitem->dwItemData)
861             {
862                 bmp = (HBITMAP)lpitem->dwItemData;
863                 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
864             }
865             else
866             {
867                 static HBITMAP hBmpSysMenu;
868
869                 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
870                 bmp = hBmpSysMenu;
871                 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
872                 /* only use right half of the bitmap */
873                 bmp_xoffset = bm.bmWidth / 2;
874                 bm.bmWidth -= bmp_xoffset;
875             }
876             goto got_bitmap;
877         case (INT_PTR)HBMMENU_MBAR_RESTORE:
878             flags = DFCS_CAPTIONRESTORE;
879             break;
880         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
881             flags = DFCS_CAPTIONMIN;
882             break;
883         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
884             flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
885             break;
886         case (INT_PTR)HBMMENU_MBAR_CLOSE:
887             flags = DFCS_CAPTIONCLOSE;
888             break;
889         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
890             flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
891             break;
892         case (INT_PTR)HBMMENU_CALLBACK:
893             {
894                 DRAWITEMSTRUCT drawItem;
895                 drawItem.CtlType = ODT_MENU;
896                 drawItem.CtlID = 0;
897                 drawItem.itemID = lpitem->wID;
898                 drawItem.itemAction = odaction;
899                 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
900                 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
901                 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
902                 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
903                 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
904                 drawItem.hwndItem = (HWND)hmenu;
905                 drawItem.hDC = hdc;
906                 drawItem.itemData = lpitem->dwItemData;
907                 drawItem.rcItem = *rect;
908                 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
909                 return;
910             }
911             break;
912         case (INT_PTR)HBMMENU_POPUP_CLOSE:
913         case (INT_PTR)HBMMENU_POPUP_RESTORE:
914         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
915         case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
916         default:
917             FIXME("Magic %p not implemented\n", hbmToDraw);
918             return;
919         }
920         r = *rect;
921         InflateRect( &r, -1, -1 );
922         if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
923         DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
924         return;
925     }
926
927     if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928
929  got_bitmap:
930     hdcMem = CreateCompatibleDC( hdc );
931     SelectObject( hdcMem, bmp );
932
933     /* handle fontsize > bitmap_height */
934     top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
935     left=rect->left;
936     rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
937     if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
938         SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
939     BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
940     DeleteDC( hdcMem );
941 }
942
943
944 /***********************************************************************
945  *           MENU_CalcItemSize
946  *
947  * Calculate the size of the menu item and store it in lpitem->rect.
948  */
949 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
950                                INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 {
952     WCHAR *p;
953     UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
954     UINT arrow_bitmap_width;
955     BITMAP bm;
956     INT itemheight;
957
958     TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
959     debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
960                          (menuBar ? " (MenuBar)" : ""));
961
962     GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
963     arrow_bitmap_width = bm.bmWidth;
964
965     /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
966     if( !menucharsize.cx ) {
967         menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
968         /* Win95/98/ME will use menucharsize.cy here. Testing is possible
969          * but it is unlikely an application will depend on that */
970         ODitemheight = HIWORD( GetDialogBaseUnits());
971     }
972
973     SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974
975     if (lpitem->fType & MF_OWNERDRAW)
976     {
977         MEASUREITEMSTRUCT mis;
978         mis.CtlType    = ODT_MENU;
979         mis.CtlID      = 0;
980         mis.itemID     = lpitem->wID;
981         mis.itemData   = lpitem->dwItemData;
982         mis.itemHeight = ODitemheight;
983         mis.itemWidth  = 0;
984         SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
985         /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
986          * width of a menufont character to the width of an owner-drawn menu. 
987          */
988         lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
989         if (menuBar) {
990             /* under at least win95 you seem to be given a standard
991                height for the menu and the height value is ignored */
992             lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
993         } else
994             lpitem->rect.bottom += mis.itemHeight;
995
996         TRACE("id=%04lx size=%dx%d\n",
997                 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
998                 lpitem->rect.bottom-lpitem->rect.top);
999         return;
1000     }
1001
1002     if (lpitem->fType & MF_SEPARATOR)
1003     {
1004         lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1005         if( !menuBar)
1006             lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1007         return;
1008     }
1009
1010     itemheight = 0;
1011     lpitem->xTab = 0;
1012
1013     if (!menuBar) {
1014         if (lpitem->hbmpItem) {
1015             SIZE size;
1016
1017             MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1018             /* Keep the size of the bitmap in callback mode to be able
1019              * to draw it correctly */
1020             lpitem->bmpsize = size;
1021             lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1022             lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1023             lpitem->rect.right += size.cx + 2;
1024             itemheight = size.cy + 2;
1025         }
1026         if( !(lppop->dwStyle & MNS_NOCHECK))
1027             lpitem->rect.right += check_bitmap_width; 
1028         lpitem->rect.right += 4 + menucharsize.cx;
1029         lpitem->xTab = lpitem->rect.right;
1030         lpitem->rect.right += arrow_bitmap_width;
1031     } else if (lpitem->hbmpItem) { /* menuBar */
1032         SIZE size;
1033
1034         MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1035         lpitem->bmpsize = size;
1036         lpitem->rect.right  += size.cx;
1037         if( lpitem->text) lpitem->rect.right  += 2;
1038         itemheight = size.cy;
1039     }
1040
1041     /* it must be a text item - unless it's the system menu */
1042     if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1043         HFONT hfontOld = NULL;
1044         RECT rc = lpitem->rect;
1045         LONG txtheight, txtwidth;
1046
1047         if ( lpitem->fState & MFS_DEFAULT ) {
1048              hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049         }
1050         if (menuBar) {
1051             txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1052                     DT_SINGLELINE|DT_CALCRECT); 
1053             lpitem->rect.right  += rc.right - rc.left;
1054             itemheight = max( max( itemheight, txtheight),
1055                     GetSystemMetrics( SM_CYMENU) - 1);
1056             lpitem->rect.right +=  2 * menucharsize.cx;
1057         } else {
1058             if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1059                 RECT tmprc = rc;
1060                 LONG tmpheight;
1061                 int n = (int)( p - lpitem->text);
1062                 /* Item contains a tab (only meaningful in popup menus) */
1063                 /* get text size before the tab */
1064                 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1065                         DT_SINGLELINE|DT_CALCRECT);
1066                 txtwidth = rc.right - rc.left;
1067                 p += 1; /* advance past the Tab */
1068                 /* get text size after the tab */
1069                 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1070                         DT_SINGLELINE|DT_CALCRECT);
1071                 lpitem->xTab += txtwidth;
1072                 txtheight = max( txtheight, tmpheight);
1073                 txtwidth += menucharsize.cx + /* space for the tab */
1074                     tmprc.right - tmprc.left; /* space for the short cut */
1075             } else {
1076                 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1077                         DT_SINGLELINE|DT_CALCRECT);
1078                 txtwidth = rc.right - rc.left;
1079                 lpitem->xTab += txtwidth;
1080             }
1081             lpitem->rect.right  += 2 + txtwidth;
1082             itemheight = max( itemheight,
1083                     max( txtheight + 2, menucharsize.cy + 4));
1084         }
1085         if (hfontOld) SelectObject (hdc, hfontOld);
1086     } else if( menuBar) {
1087         itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088     }
1089     lpitem->rect.bottom += itemheight;
1090     TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1091 }
1092
1093
1094 /***********************************************************************
1095  *           MENU_GetMaxPopupHeight
1096  */
1097 static UINT
1098 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1099 {
1100     if (lppop->cyMax)
1101         return lppop->cyMax;
1102     return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1103 }
1104
1105
1106 /***********************************************************************
1107  *           MENU_PopupMenuCalcSize
1108  *
1109  * Calculate the size of a popup menu.
1110  */
1111 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1112 {
1113     MENUITEM *lpitem;
1114     HDC hdc;
1115     int start, i;
1116     int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117
1118     lppop->Width = lppop->Height = 0;
1119     if (lppop->nItems == 0) return;
1120     hdc = GetDC( 0 );
1121
1122     SelectObject( hdc, get_menu_font(FALSE));
1123
1124     start = 0;
1125     maxX = 2 + 1;
1126
1127     lppop->maxBmpSize.cx = 0;
1128     lppop->maxBmpSize.cy = 0;
1129
1130     while (start < lppop->nItems)
1131     {
1132         lpitem = &lppop->items[start];
1133         orgX = maxX;
1134         if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1135             orgX += MENU_COL_SPACE; 
1136         orgY = MENU_TOP_MARGIN;
1137
1138         maxTab = maxTabWidth = 0;
1139           /* Parse items until column break or end of menu */
1140         for (i = start; i < lppop->nItems; i++, lpitem++)
1141         {
1142             if ((i != start) &&
1143                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144
1145             MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1146             maxX = max( maxX, lpitem->rect.right );
1147             orgY = lpitem->rect.bottom;
1148             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149             {
1150                 maxTab = max( maxTab, lpitem->xTab );
1151                 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1152             }
1153         }
1154
1155           /* Finish the column (set all items to the largest width found) */
1156         maxX = max( maxX, maxTab + maxTabWidth );
1157         for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158         {
1159             lpitem->rect.right = maxX;
1160             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1161                 lpitem->xTab = maxTab;
1162
1163         }
1164         lppop->Height = max( lppop->Height, orgY );
1165     }
1166
1167     lppop->Width  = maxX;
1168
1169     /* space for 3d border */
1170     lppop->Height += MENU_BOTTOM_MARGIN;
1171     lppop->Width += 2;
1172
1173     /* Adjust popup height if it exceeds maximum */
1174     maxHeight = MENU_GetMaxPopupHeight(lppop);
1175     lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1176     if (lppop->Height >= maxHeight)
1177     {
1178         lppop->Height = maxHeight;
1179         lppop->bScrolling = TRUE;
1180     }
1181     else
1182     {
1183         lppop->bScrolling = FALSE;
1184     }
1185
1186     ReleaseDC( 0, hdc );
1187 }
1188
1189
1190 /***********************************************************************
1191  *           MENU_MenuBarCalcSize
1192  *
1193  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194  * height is off by 1 pixel which causes lengthy window relocations when
1195  * active document window is maximized/restored.
1196  *
1197  * Calculate the size of the menu bar.
1198  */
1199 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1200                                   LPPOPUPMENU lppop, HWND hwndOwner )
1201 {
1202     MENUITEM *lpitem;
1203     int start, i, orgX, orgY, maxY, helpPos;
1204
1205     if ((lprect == NULL) || (lppop == NULL)) return;
1206     if (lppop->nItems == 0) return;
1207     TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1208     lppop->Width  = lprect->right - lprect->left;
1209     lppop->Height = 0;
1210     maxY = lprect->top+1;
1211     start = 0;
1212     helpPos = -1;
1213     lppop->maxBmpSize.cx = 0;
1214     lppop->maxBmpSize.cy = 0;
1215     while (start < lppop->nItems)
1216     {
1217         lpitem = &lppop->items[start];
1218         orgX = lprect->left;
1219         orgY = maxY;
1220
1221           /* Parse items until line break or end of menu */
1222         for (i = start; i < lppop->nItems; i++, lpitem++)
1223         {
1224             if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1225             if ((i != start) &&
1226                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227
1228             TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1229             debug_print_menuitem ("  item: ", lpitem, "");
1230             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231
1232             if (lpitem->rect.right > lprect->right)
1233             {
1234                 if (i != start) break;
1235                 else lpitem->rect.right = lprect->right;
1236             }
1237             maxY = max( maxY, lpitem->rect.bottom );
1238             orgX = lpitem->rect.right;
1239         }
1240
1241           /* Finish the line (set all items to the largest height found) */
1242         while (start < i) lppop->items[start++].rect.bottom = maxY;
1243     }
1244
1245     lprect->bottom = maxY;
1246     lppop->Height = lprect->bottom - lprect->top;
1247
1248     /* Flush right all items between the MF_RIGHTJUSTIFY and */
1249     /* the last item (if several lines, only move the last line) */
1250     lpitem = &lppop->items[lppop->nItems-1];
1251     orgY = lpitem->rect.top;
1252     orgX = lprect->right;
1253     for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1254         if ( (helpPos==-1) || (helpPos>i) )
1255             break;                              /* done */
1256         if (lpitem->rect.top != orgY) break;    /* Other line */
1257         if (lpitem->rect.right >= orgX) break;  /* Too far right already */
1258         lpitem->rect.left += orgX - lpitem->rect.right;
1259         lpitem->rect.right = orgX;
1260         orgX = lpitem->rect.left;
1261     }
1262 }
1263
1264
1265 /***********************************************************************
1266  *           MENU_DrawScrollArrows
1267  *
1268  * Draw scroll arrows.
1269  */
1270 static void
1271 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1272 {
1273     HDC hdcMem = CreateCompatibleDC(hdc);
1274     HBITMAP hOrigBitmap;
1275     UINT arrow_bitmap_width, arrow_bitmap_height;
1276     BITMAP bmp;
1277     RECT rect;
1278
1279     GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1280     arrow_bitmap_width = bmp.bmWidth;
1281     arrow_bitmap_height = bmp.bmHeight;
1282
1283     
1284     if (lppop->nScrollPos)
1285         hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1286     else
1287         hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1288     rect.left = 0;
1289     rect.top = 0;
1290     rect.right = lppop->Width;
1291     rect.bottom = arrow_bitmap_height;
1292     FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1293     BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1294            arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1295     rect.top = lppop->Height - arrow_bitmap_height;
1296     rect.bottom = lppop->Height;
1297     FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1298     if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1299         SelectObject(hdcMem, get_down_arrow_bitmap());
1300     else
1301         SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1302     BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1303            lppop->Height - arrow_bitmap_height,
1304            arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1305     SelectObject(hdcMem, hOrigBitmap);
1306     DeleteDC(hdcMem);
1307 }
1308
1309
1310 /***********************************************************************
1311  *           draw_popup_arrow
1312  *
1313  * Draws the popup-menu arrow.
1314  */
1315 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1316         UINT arrow_bitmap_height)
1317 {
1318     HDC hdcMem = CreateCompatibleDC( hdc );
1319     HBITMAP hOrigBitmap;
1320
1321     hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1322     BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1323             (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1324             arrow_bitmap_width, arrow_bitmap_height,
1325             hdcMem, 0, 0, SRCCOPY );
1326     SelectObject( hdcMem, hOrigBitmap );
1327     DeleteDC( hdcMem );
1328 }
1329 /***********************************************************************
1330  *           MENU_DrawMenuItem
1331  *
1332  * Draw a single menu item.
1333  */
1334 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1335                                UINT height, BOOL menuBar, UINT odaction )
1336 {
1337     RECT rect;
1338     BOOL flat_menu = FALSE;
1339     int bkgnd;
1340     UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1341     POPUPMENU *menu = MENU_GetMenu(hmenu);
1342     RECT bmprc;
1343
1344     debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1345
1346     if (!menuBar) {
1347         BITMAP bmp;
1348         GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1349         arrow_bitmap_width = bmp.bmWidth;
1350         arrow_bitmap_height = bmp.bmHeight;
1351     }
1352
1353     if (lpitem->fType & MF_SYSMENU)
1354     {
1355         if( !IsIconic(hwnd) )
1356             NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1357         return;
1358     }
1359
1360     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1361     bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1362   
1363       /* Setup colors */
1364
1365     if (lpitem->fState & MF_HILITE)
1366     {
1367         if(menuBar && !flat_menu) {
1368             SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1369             SetBkColor(hdc, GetSysColor(COLOR_MENU));
1370         } else {
1371             if(lpitem->fState & MF_GRAYED)
1372                 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1373             else
1374                 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1375             SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1376         }
1377     }
1378     else
1379     {
1380         if (lpitem->fState & MF_GRAYED)
1381             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1382         else
1383             SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1384         SetBkColor( hdc, GetSysColor( bkgnd ) );
1385     }
1386
1387     TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1388     rect = lpitem->rect;
1389     MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390
1391     if (lpitem->fType & MF_OWNERDRAW)
1392     {
1393         /*
1394         ** Experimentation under Windows reveals that an owner-drawn
1395         ** menu is given the rectangle which includes the space it requested
1396         ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397         ** and a popup-menu arrow.  This is the value of lpitem->rect.
1398         ** Windows will leave all drawing to the application except for
1399         ** the popup-menu arrow.  Windows always draws that itself, after
1400         ** the menu owner has finished drawing.
1401         */
1402         DRAWITEMSTRUCT dis;
1403
1404         dis.CtlType   = ODT_MENU;
1405         dis.CtlID     = 0;
1406         dis.itemID    = lpitem->wID;
1407         dis.itemData  = lpitem->dwItemData;
1408         dis.itemState = 0;
1409         if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1410         if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1411         if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
1412         dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413         dis.hwndItem   = (HWND)hmenu;
1414         dis.hDC        = hdc;
1415         dis.rcItem     = rect;
1416         TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417               "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1418               dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1419               dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1420         SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1421         /* Draw the popup-menu arrow */
1422         if (lpitem->fType & MF_POPUP)
1423             draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1424                     arrow_bitmap_height);
1425         return;
1426     }
1427
1428     if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429
1430     if (lpitem->fState & MF_HILITE)
1431     {
1432         if (flat_menu)
1433         {
1434             InflateRect (&rect, -1, -1);
1435             FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1436             InflateRect (&rect, 1, 1);
1437             FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438         }
1439         else
1440         {
1441             if(menuBar)
1442                 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1443             else
1444                 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1445         }
1446     }
1447     else
1448         FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449
1450     SetBkMode( hdc, TRANSPARENT );
1451
1452     /* vertical separator */
1453     if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1454     {
1455         HPEN oldPen;
1456         RECT rc = rect;
1457
1458         rc.left -= MENU_COL_SPACE / 2 + 1;
1459         rc.top = 3;
1460         rc.bottom = height - 3;
1461         if (flat_menu)
1462         {
1463             oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1464             MoveToEx( hdc, rc.left, rc.top, NULL );
1465             LineTo( hdc, rc.left, rc.bottom );
1466             SelectObject( hdc, oldPen );
1467         }
1468         else
1469             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1470     }
1471
1472     /* horizontal separator */
1473     if (lpitem->fType & MF_SEPARATOR)
1474     {
1475         HPEN oldPen;
1476         RECT rc = rect;
1477
1478         rc.left++;
1479         rc.right--;
1480         rc.top = ( rc.top + rc.bottom) / 2;
1481         if (flat_menu)
1482         {
1483             oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1484             MoveToEx( hdc, rc.left, rc.top, NULL );
1485             LineTo( hdc, rc.right, rc.top );
1486             SelectObject( hdc, oldPen );
1487         }
1488         else
1489             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1490         return;
1491     }
1492
1493         /* helper lines for debugging */
1494 /*      FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1495         SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1496         MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1497         LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1498 */
1499
1500     if (lpitem->hbmpItem) {
1501         /* calculate the bitmap rectangle in coordinates relative
1502          * to the item rectangle */
1503         if( menuBar) {
1504             if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1505                 bmprc.left = 3;
1506             else 
1507                 bmprc.left = lpitem->text ? menucharsize.cx : 0;          
1508         } else {
1509             bmprc.left = 4;
1510             if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1511                 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK); 
1512         }
1513         bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
1514         if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1515             bmprc.top = 0;
1516         else
1517             bmprc.top = (rect.bottom - rect.top -
1518                     lpitem->bmpsize.cy) / 2; 
1519         bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
1520     }
1521
1522     if (!menuBar)
1523     {
1524         HBITMAP bm;
1525         INT y = rect.top + rect.bottom;
1526         RECT rc = rect;
1527         int checked = FALSE;
1528         UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1529         UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1530         /* Draw the check mark
1531          *
1532          * FIXME:
1533          * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534          */
1535         if( !(menu->dwStyle & MNS_NOCHECK)) {
1536             bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1537                 lpitem->hUnCheckBit;
1538             if (bm)  /* we have a custom bitmap */
1539             {
1540                 HDC hdcMem = CreateCompatibleDC( hdc );
1541
1542                 SelectObject( hdcMem, bm );
1543                 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1544                         check_bitmap_width, check_bitmap_height,
1545                         hdcMem, 0, 0, SRCCOPY );
1546                 DeleteDC( hdcMem );
1547                 checked = TRUE;
1548             }
1549             else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1550             {
1551                 RECT r;
1552                 HBITMAP bm = CreateBitmap( check_bitmap_width,
1553                         check_bitmap_height, 1, 1, NULL );
1554                 HDC hdcMem = CreateCompatibleDC( hdc );
1555
1556                 SelectObject( hdcMem, bm );
1557                 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1558                 DrawFrameControl( hdcMem, &r, DFC_MENU,
1559                         (lpitem->fType & MFT_RADIOCHECK) ?
1560                         DFCS_MENUBULLET : DFCS_MENUCHECK );
1561                 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1562                         hdcMem, 0, 0, SRCCOPY );
1563                 DeleteDC( hdcMem );
1564                 DeleteObject( bm );
1565                 checked = TRUE;
1566             }
1567         }
1568         if( lpitem->hbmpItem &&
1569                 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1570             POINT origorg;
1571             /* some applications make this assumption on the DC's origin */
1572             SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1573             MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1574                     odaction, FALSE);
1575             SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576         }
1577         /* Draw the popup-menu arrow */
1578         if (lpitem->fType & MF_POPUP)
1579             draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1580                     arrow_bitmap_height);
1581         rect.left += 4;
1582         if( !(menu->dwStyle & MNS_NOCHECK))
1583             rect.left += check_bitmap_width;
1584         rect.right -= arrow_bitmap_width;
1585     }
1586     else if( lpitem->hbmpItem)
1587     {   /* Draw the bitmap */
1588         POINT origorg;
1589         
1590         SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1591         MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1592                 odaction, menuBar);
1593         SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594     }
1595     /* process text if present */
1596     if (lpitem->text)
1597     {
1598         register int i;
1599         HFONT hfontOld = 0;
1600
1601         UINT uFormat = (menuBar) ?
1602                         DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1603                         DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604
1605         if( !(menu->dwStyle & MNS_CHECKORBMP))
1606             rect.left += menu->maxBmpSize.cx;
1607
1608         if ( lpitem->fState & MFS_DEFAULT )
1609         {
1610              hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1611         }
1612
1613         if (menuBar) {
1614             if( lpitem->hbmpItem)
1615                 rect.left += lpitem->bmpsize.cx;
1616             if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1617                 rect.left += menucharsize.cx;
1618             rect.right -= menucharsize.cx;
1619         }
1620
1621         for (i = 0; lpitem->text[i]; i++)
1622             if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1623                 break;
1624
1625         if(lpitem->fState & MF_GRAYED)
1626         {
1627             if (!(lpitem->fState & MF_HILITE) )
1628             {
1629                 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1630                 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1631                 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1632                 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633             }
1634             SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1635         }
1636
1637         DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638
1639         /* paint the shortcut text */
1640         if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
1641         {
1642             if (lpitem->text[i] == '\t')
1643             {
1644                 rect.left = lpitem->xTab;
1645                 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1646             }
1647             else
1648             {
1649                 rect.right = lpitem->xTab;
1650                 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1651             }
1652
1653             if(lpitem->fState & MF_GRAYED)
1654             {
1655                 if (!(lpitem->fState & MF_HILITE) )
1656                 {
1657                     ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1658                     SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1659                     DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1660                     --rect.left; --rect.top; --rect.right; --rect.bottom;
1661                 }
1662                 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663             }
1664             DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1665         }
1666
1667         if (hfontOld)
1668             SelectObject (hdc, hfontOld);
1669     }
1670 }
1671
1672
1673 /***********************************************************************
1674  *           MENU_DrawPopupMenu
1675  *
1676  * Paint a popup menu.
1677  */
1678 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 {
1680     HBRUSH hPrevBrush = 0;
1681     RECT rect;
1682
1683     TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684
1685     GetClientRect( hwnd, &rect );
1686
1687     if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1688         && (SelectObject( hdc, get_menu_font(FALSE))))
1689     {
1690         HPEN hPrevPen;
1691
1692         Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693
1694         hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1695         if( hPrevPen )
1696         {
1697             POPUPMENU *menu;
1698             BOOL flat_menu = FALSE;
1699
1700             SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1701             if (flat_menu)
1702                 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1703             else
1704                 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705
1706             if( (menu = MENU_GetMenu( hmenu )))
1707             {
1708                 /* draw menu items */
1709                 if( menu->nItems)
1710                 {
1711                     MENUITEM *item;
1712                     UINT u;
1713
1714                     item = menu->items;
1715                     for( u = menu->nItems; u > 0; u--, item++)
1716                         MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1717                                 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718                 }
1719                 /* draw scroll arrows */
1720                 if (menu->bScrolling)
1721                     MENU_DrawScrollArrows(menu, hdc);
1722             }
1723         } else
1724         {
1725             SelectObject( hdc, hPrevBrush );
1726         }
1727     }
1728 }
1729
1730 /***********************************************************************
1731  *           MENU_DrawMenuBar
1732  *
1733  * Paint a menu bar. Returns the height of the menu bar.
1734  * called from [windows/nonclient.c]
1735  */
1736 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1737                          BOOL suppress_draw)
1738 {
1739     LPPOPUPMENU lppop;
1740     HFONT hfontOld = 0;
1741     HMENU hMenu = GetMenu(hwnd);
1742
1743     lppop = MENU_GetMenu( hMenu );
1744     if (lppop == NULL || lprect == NULL)
1745     {
1746         return GetSystemMetrics(SM_CYMENU);
1747     }
1748
1749     if (suppress_draw)
1750     {
1751         hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752
1753         if (lppop->Height == 0)
1754                 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755
1756         lprect->bottom = lprect->top + lppop->Height;
1757
1758         if (hfontOld) SelectObject( hDC, hfontOld);
1759         return lppop->Height;
1760     }
1761     else
1762         return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1763 }
1764
1765
1766 /***********************************************************************
1767  *           MENU_ShowPopup
1768  *
1769  * Display a popup menu.
1770  */
1771 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1772                               INT x, INT y, INT xanchor, INT yanchor )
1773 {
1774     POPUPMENU *menu;
1775     INT width, height;
1776     POINT pt;
1777     HMONITOR monitor;
1778     MONITORINFO info;
1779
1780     TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1781           hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1782
1783     if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1784     if (menu->FocusedItem != NO_SELECTED_ITEM)
1785     {
1786         menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1787         menu->FocusedItem = NO_SELECTED_ITEM;
1788     }
1789
1790     /* store the owner for DrawItem */
1791     menu->hwndOwner = hwndOwner;
1792
1793     menu->nScrollPos = 0;
1794     MENU_PopupMenuCalcSize( menu );
1795
1796     /* adjust popup menu pos so that it fits within the desktop */
1797
1798     width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1799     height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1800
1801     /* FIXME: should use item rect */
1802     pt.x = x;
1803     pt.y = y;
1804     monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1805     info.cbSize = sizeof(info);
1806     GetMonitorInfoW( monitor, &info );
1807     if( x + width > info.rcWork.right)
1808     {
1809         if( xanchor && x >= width - xanchor )
1810             x -= width - xanchor;
1811
1812         if( x + width > info.rcWork.right)
1813             x = info.rcWork.right - width;
1814     }
1815     if( x < info.rcWork.left ) x = info.rcWork.left;
1816
1817     if( y + height > info.rcWork.bottom)
1818     {
1819         if( yanchor && y >= height + yanchor )
1820             y -= height + yanchor;
1821
1822         if( y + height > info.rcWork.bottom)
1823             y = info.rcWork.bottom - height;
1824     }
1825     if( y < info.rcWork.top ) y = info.rcWork.top;
1826
1827     /* NOTE: In Windows, top menu popup is not owned. */
1828     menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1829                                 WS_POPUP, x, y, width, height,
1830                                 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1831                                 (LPVOID)hmenu );
1832     if( !menu->hWnd ) return FALSE;
1833     if (!top_popup) top_popup = menu->hWnd;
1834
1835     /* Display the window */
1836
1837     SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1838                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1839     UpdateWindow( menu->hWnd );
1840     return TRUE;
1841 }
1842
1843
1844 /***********************************************************************
1845  *           MENU_EnsureMenuItemVisible
1846  */
1847 static void
1848 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1849 {
1850     if (lppop->bScrolling)
1851     {
1852         MENUITEM *item = &lppop->items[wIndex];
1853         UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1854         UINT nOldPos = lppop->nScrollPos;
1855         RECT rc;
1856         UINT arrow_bitmap_height;
1857         BITMAP bmp;
1858         
1859         GetClientRect(lppop->hWnd, &rc);
1860
1861         GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1862         arrow_bitmap_height = bmp.bmHeight;
1863
1864         rc.top += arrow_bitmap_height;
1865         rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1866        
1867         nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1868         if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1869         {
1870             
1871             lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1872             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1873             MENU_DrawScrollArrows(lppop, hdc);
1874         }
1875         else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1876         {
1877             lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1878             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1879             MENU_DrawScrollArrows(lppop, hdc);
1880         }
1881     }
1882 }
1883
1884
1885 /***********************************************************************
1886  *           MENU_SelectItem
1887  */
1888 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1889                              BOOL sendMenuSelect, HMENU topmenu )
1890 {
1891     LPPOPUPMENU lppop;
1892     HDC hdc;
1893
1894     TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1895
1896     lppop = MENU_GetMenu( hmenu );
1897     if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1898
1899     if (lppop->FocusedItem == wIndex) return;
1900     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1901     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1902     if (!top_popup) top_popup = lppop->hWnd;
1903
1904     SelectObject( hdc, get_menu_font(FALSE));
1905
1906       /* Clear previous highlighted item */
1907     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1908     {
1909         lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1910         MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1911                           lppop->Height, !(lppop->wFlags & MF_POPUP),
1912                           ODA_SELECT );
1913     }
1914
1915       /* Highlight new item (if any) */
1916     lppop->FocusedItem = wIndex;
1917     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1918     {
1919         if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1920             lppop->items[wIndex].fState |= MF_HILITE;
1921             MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1922             MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1923                     &lppop->items[wIndex], lppop->Height,
1924                     !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1925         }
1926         if (sendMenuSelect)
1927         {
1928             MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1929             SendMessageW( hwndOwner, WM_MENUSELECT,
1930                      MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1931                      ip->fType | ip->fState |
1932                      (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1933         }
1934     }
1935     else if (sendMenuSelect) {
1936         if(topmenu){
1937             int pos;
1938             if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1939                 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1940                 MENUITEM *ip = &ptm->items[pos];
1941                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1942                          ip->fType | ip->fState |
1943                          (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1944             }
1945         }
1946     }
1947     ReleaseDC( lppop->hWnd, hdc );
1948 }
1949
1950
1951 /***********************************************************************
1952  *           MENU_MoveSelection
1953  *
1954  * Moves currently selected item according to the offset parameter.
1955  * If there is no selection then it should select the last item if
1956  * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1957  */
1958 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1959 {
1960     INT i;
1961     POPUPMENU *menu;
1962
1963     TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1964
1965     menu = MENU_GetMenu( hmenu );
1966     if ((!menu) || (!menu->items)) return;
1967
1968     if ( menu->FocusedItem != NO_SELECTED_ITEM )
1969     {
1970         if( menu->nItems == 1 ) return; else
1971         for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1972                                             ; i += offset)
1973             if (!(menu->items[i].fType & MF_SEPARATOR))
1974             {
1975                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1976                 return;
1977             }
1978     }
1979
1980     for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1981                   i >= 0 && i < menu->nItems ; i += offset)
1982         if (!(menu->items[i].fType & MF_SEPARATOR))
1983         {
1984             MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1985             return;
1986         }
1987 }
1988
1989
1990 /**********************************************************************
1991  *         MENU_SetItemData
1992  *
1993  * Set an item's flags, id and text ptr. Called by InsertMenu() and
1994  * ModifyMenu().
1995  */
1996 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1997                                 LPCWSTR str )
1998 {
1999     debug_print_menuitem("MENU_SetItemData from: ", item, "");
2000     TRACE("flags=%x str=%p\n", flags, str);
2001
2002     if (IS_STRING_ITEM(flags))
2003     {
2004         LPWSTR prevText = item->text;
2005         if (!str)
2006         {
2007             flags |= MF_SEPARATOR;
2008             item->text = NULL;
2009         }
2010         else
2011         {
2012             LPWSTR text;
2013             /* Item beginning with a backspace is a help item */
2014             if (*str == '\b')
2015             {
2016                 flags |= MF_HELP;
2017                 str++;
2018             }
2019             if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2020                 return FALSE;
2021             strcpyW( text, str );
2022             item->text = text;
2023         }
2024         item->hbmpItem = NULL;
2025         HeapFree( GetProcessHeap(), 0, prevText );
2026     }
2027     else if(( flags & MFT_BITMAP)) {
2028         item->hbmpItem = HBITMAP_32(LOWORD(str));
2029         /* setting bitmap clears text */
2030         HeapFree( GetProcessHeap(), 0, item->text );
2031         item->text = NULL;
2032     }
2033
2034     if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2035
2036     if (flags & MF_OWNERDRAW)
2037         item->dwItemData = (DWORD_PTR)str;
2038     else
2039         item->dwItemData = 0;
2040
2041     if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2042         DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */
2043
2044     if (flags & MF_POPUP)
2045     {
2046         POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2047         if (menu) menu->wFlags |= MF_POPUP;
2048         else
2049         {
2050             item->wID = 0;
2051             item->hSubMenu = 0;
2052             item->fType = 0;
2053             item->fState = 0;
2054             return FALSE;
2055         }
2056     }
2057
2058     item->wID = id;
2059     if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2060
2061     if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2062       flags |= MF_POPUP; /* keep popup */
2063
2064     item->fType = flags & TYPE_MASK;
2065     /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2066        for ModifyMenu, but Windows accepts it */
2067     item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2068
2069     /* Don't call SetRectEmpty here! */
2070
2071     debug_print_menuitem("MENU_SetItemData to  : ", item, "");
2072     return TRUE;
2073 }
2074
2075
2076 /**********************************************************************
2077  *         MENU_InsertItem
2078  *
2079  * Insert (allocate) a new item into a menu.
2080  */
2081 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2082 {
2083     MENUITEM *newItems;
2084     POPUPMENU *menu;
2085
2086     if (!(menu = MENU_GetMenu(hMenu)))
2087         return NULL;
2088
2089     /* Find where to insert new item */
2090
2091     if (flags & MF_BYPOSITION) {
2092         if (pos > menu->nItems)
2093             pos = menu->nItems;
2094     } else {
2095         if (!MENU_FindItem( &hMenu, &pos, flags ))
2096             pos = menu->nItems;
2097         else {
2098             if (!(menu = MENU_GetMenu( hMenu )))
2099                 return NULL;
2100         }
2101     }
2102
2103     /* Make sure that MDI system buttons stay on the right side.
2104      * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2105      * regardless of their id.
2106      */
2107     while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2108            (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2109            (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2110         pos--;
2111
2112     TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2113
2114     /* Create new items array */
2115
2116     newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2117     if (!newItems)
2118     {
2119         WARN("allocation failed\n" );
2120         return NULL;
2121     }
2122     if (menu->nItems > 0)
2123     {
2124           /* Copy the old array into the new one */
2125         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2126         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2127                                         (menu->nItems-pos)*sizeof(MENUITEM) );
2128         HeapFree( GetProcessHeap(), 0, menu->items );
2129     }
2130     menu->items = newItems;
2131     menu->nItems++;
2132     memset( &newItems[pos], 0, sizeof(*newItems) );
2133     menu->Height = 0; /* force size recalculate */
2134     return &newItems[pos];
2135 }
2136
2137
2138 /**********************************************************************
2139  *         MENU_ParseResource
2140  *
2141  * Parse a standard menu resource and add items to the menu.
2142  * Return a pointer to the end of the resource.
2143  *
2144  * NOTE: flags is equivalent to the mtOption field
2145  */
2146 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2147 {
2148     WORD flags, id = 0;
2149     LPCSTR str;
2150     BOOL end_flag;
2151
2152     do
2153     {
2154         flags = GET_WORD(res);
2155         end_flag = flags & MF_END;
2156         /* Remove MF_END because it has the same value as MF_HILITE */
2157         flags &= ~MF_END;
2158         res += sizeof(WORD);
2159         if (!(flags & MF_POPUP))
2160         {
2161             id = GET_WORD(res);
2162             res += sizeof(WORD);
2163         }
2164         str = res;
2165         if (!unicode) res += strlen(str) + 1;
2166         else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2167         if (flags & MF_POPUP)
2168         {
2169             HMENU hSubMenu = CreatePopupMenu();
2170             if (!hSubMenu) return NULL;
2171             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2172                 return NULL;
2173             if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2174             else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2175         }
2176         else  /* Not a popup */
2177         {
2178             if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2179             else AppendMenuW( hMenu, flags, id,
2180                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2181         }
2182     } while (!end_flag);
2183     return res;
2184 }
2185
2186
2187 /**********************************************************************
2188  *         MENUEX_ParseResource
2189  *
2190  * Parse an extended menu resource and add items to the menu.
2191  * Return a pointer to the end of the resource.
2192  */
2193 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2194 {
2195     WORD resinfo;
2196     do {
2197         MENUITEMINFOW mii;
2198
2199         mii.cbSize = sizeof(mii);
2200         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2201         mii.fType = GET_DWORD(res);
2202         res += sizeof(DWORD);
2203         mii.fState = GET_DWORD(res);
2204         res += sizeof(DWORD);
2205         mii.wID = GET_DWORD(res);
2206         res += sizeof(DWORD);
2207         resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
2208         res += sizeof(WORD);
2209         /* Align the text on a word boundary.  */
2210         res += (~((UINT_PTR)res - 1)) & 1;
2211         mii.dwTypeData = (LPWSTR) res;
2212         res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2213         /* Align the following fields on a dword boundary.  */
2214         res += (~((UINT_PTR)res - 1)) & 3;
2215
2216         TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2217               mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2218
2219         if (resinfo & 1) {      /* Pop-up? */
2220             /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
2221             res += sizeof(DWORD);
2222             mii.hSubMenu = CreatePopupMenu();
2223             if (!mii.hSubMenu)
2224                 return NULL;
2225             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2226                 DestroyMenu(mii.hSubMenu);
2227                 return NULL;
2228             }
2229             mii.fMask |= MIIM_SUBMENU;
2230             mii.fType |= MF_POPUP;
2231         }
2232         else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2233         {
2234             WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2235                 mii.wID, mii.fType);
2236             mii.fType |= MF_SEPARATOR;
2237         }
2238         InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2239     } while (!(resinfo & MF_END));
2240     return res;
2241 }
2242
2243
2244 /***********************************************************************
2245  *           MENU_GetSubPopup
2246  *
2247  * Return the handle of the selected sub-popup menu (if any).
2248  */
2249 static HMENU MENU_GetSubPopup( HMENU hmenu )
2250 {
2251     POPUPMENU *menu;
2252     MENUITEM *item;
2253
2254     menu = MENU_GetMenu( hmenu );
2255
2256     if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2257
2258     item = &menu->items[menu->FocusedItem];
2259     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2260         return item->hSubMenu;
2261     return 0;
2262 }
2263
2264
2265 /***********************************************************************
2266  *           MENU_HideSubPopups
2267  *
2268  * Hide the sub-popup menus of this menu.
2269  */
2270 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2271                                 BOOL sendMenuSelect )
2272 {
2273     POPUPMENU *menu = MENU_GetMenu( hmenu );
2274
2275     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2276
2277     if (menu && top_popup)
2278     {
2279         HMENU hsubmenu;
2280         POPUPMENU *submenu;
2281         MENUITEM *item;
2282
2283         if (menu->FocusedItem != NO_SELECTED_ITEM)
2284         {
2285             item = &menu->items[menu->FocusedItem];
2286             if (!(item->fType & MF_POPUP) ||
2287                 !(item->fState & MF_MOUSESELECT)) return;
2288             item->fState &= ~MF_MOUSESELECT;
2289             hsubmenu = item->hSubMenu;
2290         } else return;
2291
2292         submenu = MENU_GetMenu( hsubmenu );
2293         MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2294         MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2295         DestroyWindow( submenu->hWnd );
2296         submenu->hWnd = 0;
2297     }
2298 }
2299
2300
2301 /***********************************************************************
2302  *           MENU_ShowSubPopup
2303  *
2304  * Display the sub-menu of the selected item of this menu.
2305  * Return the handle of the submenu, or hmenu if no submenu to display.
2306  */
2307 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2308                                   BOOL selectFirst, UINT wFlags )
2309 {
2310     RECT rect;
2311     POPUPMENU *menu;
2312     MENUITEM *item;
2313     HDC hdc;
2314
2315     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2316
2317     if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2318
2319     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2320
2321     item = &menu->items[menu->FocusedItem];
2322     if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2323         return hmenu;
2324
2325     /* message must be sent before using item,
2326        because nearly everything may be changed by the application ! */
2327
2328     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2329     if (!(wFlags & TPM_NONOTIFY))
2330        SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2331                      MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2332
2333     item = &menu->items[menu->FocusedItem];
2334     rect = item->rect;
2335
2336     /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2337     if (!(item->fState & MF_HILITE))
2338     {
2339         if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2340         else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2341
2342         SelectObject( hdc, get_menu_font(FALSE));
2343
2344         item->fState |= MF_HILITE;
2345         MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2346         ReleaseDC( menu->hWnd, hdc );
2347     }
2348     if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2349       item->rect = rect;
2350
2351     item->fState |= MF_MOUSESELECT;
2352
2353     if (IS_SYSTEM_MENU(menu))
2354     {
2355         MENU_InitSysMenuPopup(item->hSubMenu,
2356                               GetWindowLongW( menu->hWnd, GWL_STYLE ),
2357                               GetClassLongW( menu->hWnd, GCL_STYLE));
2358
2359         NC_GetSysPopupPos( menu->hWnd, &rect );
2360         rect.top = rect.bottom;
2361         rect.right = GetSystemMetrics(SM_CXSIZE);
2362         rect.bottom = GetSystemMetrics(SM_CYSIZE);
2363     }
2364     else
2365     {
2366         GetWindowRect( menu->hWnd, &rect );
2367         if (menu->wFlags & MF_POPUP)
2368         {
2369             RECT rc = item->rect;
2370
2371             MENU_AdjustMenuItemRect(menu, &rc);
2372
2373             /* The first item in the popup menu has to be at the
2374                same y position as the focused menu item */
2375             rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2376             rect.top += rc.top - MENU_TOP_MARGIN;
2377             rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2378             rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2379                           - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2380         }
2381         else
2382         {
2383             rect.left += item->rect.left;
2384             rect.top += item->rect.bottom;
2385             rect.right = item->rect.right - item->rect.left;
2386             rect.bottom = item->rect.bottom - item->rect.top;
2387         }
2388     }
2389
2390     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2391                     rect.left, rect.top, rect.right, rect.bottom );
2392     if (selectFirst)
2393         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2394     return item->hSubMenu;
2395 }
2396
2397
2398
2399 /**********************************************************************
2400  *         MENU_IsMenuActive
2401  */
2402 HWND MENU_IsMenuActive(void)
2403 {
2404     return top_popup;
2405 }
2406
2407 /***********************************************************************
2408  *           MENU_PtMenu
2409  *
2410  * Walks menu chain trying to find a menu pt maps to.
2411  */
2412 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2413 {
2414    POPUPMENU *menu = MENU_GetMenu( hMenu );
2415    UINT item = menu->FocusedItem;
2416    HMENU ret;
2417
2418    /* try subpopup first (if any) */
2419    ret = (item != NO_SELECTED_ITEM &&
2420           (menu->items[item].fType & MF_POPUP) &&
2421           (menu->items[item].fState & MF_MOUSESELECT))
2422         ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2423
2424    if (!ret)  /* check the current window (avoiding WM_HITTEST) */
2425    {
2426        INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2427        if( menu->wFlags & MF_POPUP )
2428        {
2429            if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2430        }
2431        else if (ht == HTSYSMENU)
2432            ret = get_win_sys_menu( menu->hWnd );
2433        else if (ht == HTMENU)
2434            ret = GetMenu( menu->hWnd );
2435    }
2436    return ret;
2437 }
2438
2439 /***********************************************************************
2440  *           MENU_ExecFocusedItem
2441  *
2442  * Execute a menu item (for instance when user pressed Enter).
2443  * Return the wID of the executed item. Otherwise, -1 indicating
2444  * that no menu item was executed, -2 if a popup is shown;
2445  * Have to receive the flags for the TrackPopupMenu options to avoid
2446  * sending unwanted message.
2447  *
2448  */
2449 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2450 {
2451     MENUITEM *item;
2452     POPUPMENU *menu = MENU_GetMenu( hMenu );
2453
2454     TRACE("%p hmenu=%p\n", pmt, hMenu);
2455
2456     if (!menu || !menu->nItems ||
2457         (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2458
2459     item = &menu->items[menu->FocusedItem];
2460
2461     TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2462
2463     if (!(item->fType & MF_POPUP))
2464     {
2465         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2466         {
2467             /* If TPM_RETURNCMD is set you return the id, but
2468                do not send a message to the owner */
2469             if(!(wFlags & TPM_RETURNCMD))
2470             {
2471                 if( menu->wFlags & MF_SYSMENU )
2472                     PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2473                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2474                 else
2475                 {
2476                     if (menu->dwStyle & MNS_NOTIFYBYPOS)
2477                         PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2478                                       (LPARAM)hMenu);
2479                     else
2480                         PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2481                 }
2482             }
2483             return item->wID;
2484         }
2485     }
2486     else
2487     {
2488         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2489         return -2;
2490     }
2491
2492     return -1;
2493 }
2494
2495 /***********************************************************************
2496  *           MENU_SwitchTracking
2497  *
2498  * Helper function for menu navigation routines.
2499  */
2500 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2501 {
2502     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2503     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2504
2505     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2506
2507     if( pmt->hTopMenu != hPtMenu &&
2508         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2509     {
2510         /* both are top level menus (system and menu-bar) */
2511         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2512         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2513         pmt->hTopMenu = hPtMenu;
2514     }
2515     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2516     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2517 }
2518
2519
2520 /***********************************************************************
2521  *           MENU_ButtonDown
2522  *
2523  * Return TRUE if we can go on with menu tracking.
2524  */
2525 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2526 {
2527     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2528
2529     if (hPtMenu)
2530     {
2531         UINT id = 0;
2532         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2533         MENUITEM *item;
2534
2535         if( IS_SYSTEM_MENU(ptmenu) )
2536             item = ptmenu->items;
2537         else
2538             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2539
2540         if( item )
2541         {
2542             if( ptmenu->FocusedItem != id )
2543                 MENU_SwitchTracking( pmt, hPtMenu, id );
2544
2545             /* If the popup menu is not already "popped" */
2546             if(!(item->fState & MF_MOUSESELECT ))
2547             {
2548                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2549             }
2550
2551             return TRUE;
2552         }
2553         /* Else the click was on the menu bar, finish the tracking */
2554     }
2555     return FALSE;
2556 }
2557
2558 /***********************************************************************
2559  *           MENU_ButtonUp
2560  *
2561  * Return the value of MENU_ExecFocusedItem if
2562  * the selected item was not a popup. Else open the popup.
2563  * A -1 return value indicates that we go on with menu tracking.
2564  *
2565  */
2566 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2567 {
2568     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2569
2570     if (hPtMenu)
2571     {
2572         UINT id = 0;
2573         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2574         MENUITEM *item;
2575
2576         if( IS_SYSTEM_MENU(ptmenu) )
2577             item = ptmenu->items;
2578         else
2579             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2580
2581         if( item && (ptmenu->FocusedItem == id ))
2582         {
2583             debug_print_menuitem ("FocusedItem: ", item, "");
2584
2585             if( !(item->fType & MF_POPUP) )
2586             {
2587                 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2588                 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2589                 return executedMenuId;
2590             }
2591
2592             /* If we are dealing with the top-level menu            */
2593             /* and this is a click on an already "popped" item:     */
2594             /* Stop the menu tracking and close the opened submenus */
2595             if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2596                 return 0;
2597         }
2598         ptmenu->bTimeToHide = TRUE;
2599     }
2600     return -1;
2601 }
2602
2603
2604 /***********************************************************************
2605  *           MENU_MouseMove
2606  *
2607  * Return TRUE if we can go on with menu tracking.
2608  */
2609 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2610 {
2611     UINT id = NO_SELECTED_ITEM;
2612     POPUPMENU *ptmenu = NULL;
2613
2614     if( hPtMenu )
2615     {
2616         ptmenu = MENU_GetMenu( hPtMenu );
2617         if( IS_SYSTEM_MENU(ptmenu) )
2618             id = 0;
2619         else
2620             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2621     }
2622
2623     if( id == NO_SELECTED_ITEM )
2624     {
2625         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2626                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2627
2628     }
2629     else if( ptmenu->FocusedItem != id )
2630     {
2631             MENU_SwitchTracking( pmt, hPtMenu, id );
2632             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2633     }
2634     return TRUE;
2635 }
2636
2637
2638 /***********************************************************************
2639  *           MENU_SetCapture
2640  */
2641 static void MENU_SetCapture( HWND hwnd )
2642 {
2643     HWND previous = 0;
2644
2645     SERVER_START_REQ( set_capture_window )
2646     {
2647         req->handle = hwnd;
2648         req->flags  = CAPTURE_MENU;
2649         if (!wine_server_call_err( req ))
2650         {
2651             previous = reply->previous;
2652             hwnd = reply->full_handle;
2653         }
2654     }
2655     SERVER_END_REQ;
2656
2657     if (previous && previous != hwnd)
2658         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2659 }
2660
2661
2662 /***********************************************************************
2663  *           MENU_DoNextMenu
2664  *
2665  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2666  */
2667 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2668 {
2669     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2670     BOOL atEnd = FALSE;
2671
2672     /* When skipping left, we need to do something special after the
2673        first menu.                                                  */
2674     if (vk == VK_LEFT && menu->FocusedItem == 0)
2675     {
2676         atEnd = TRUE;
2677     }
2678     /* When skipping right, for the non-system menu, we need to
2679        handle the last non-special menu item (ie skip any window
2680        icons such as MDI maximize, restore or close)             */
2681     else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2682     {
2683         int i = menu->FocusedItem + 1;
2684         while (i < menu->nItems) {
2685             if ((menu->items[i].wID >= SC_SIZE &&
2686                  menu->items[i].wID <= SC_RESTORE)) {
2687                 i++;
2688             } else break;
2689         }
2690         if (i == menu->nItems) {
2691             atEnd = TRUE;
2692         }
2693     }
2694     /* When skipping right, we need to cater for the system menu */
2695     else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2696     {
2697         if (menu->FocusedItem == (menu->nItems - 1)) {
2698             atEnd = TRUE;
2699         }
2700     }
2701
2702     if( atEnd )
2703     {
2704         MDINEXTMENU next_menu;
2705         HMENU hNewMenu;
2706         HWND  hNewWnd;
2707         UINT  id = 0;
2708
2709         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2710         next_menu.hmenuNext = 0;
2711         next_menu.hwndNext = 0;
2712         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2713
2714         TRACE("%p [%p] -> %p [%p]\n",
2715               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2716
2717         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2718         {
2719             DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2720             hNewWnd = pmt->hOwnerWnd;
2721             if( IS_SYSTEM_MENU(menu) )
2722             {
2723                 /* switch to the menu bar */
2724
2725                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2726
2727                 if( vk == VK_LEFT )
2728                 {
2729                     menu = MENU_GetMenu( hNewMenu );
2730                     id = menu->nItems - 1;
2731
2732                     /* Skip backwards over any system predefined icons,
2733                        eg. MDI close, restore etc icons                 */
2734                     while ((id > 0) &&
2735                            (menu->items[id].wID >= SC_SIZE &&
2736                             menu->items[id].wID <= SC_RESTORE)) id--;
2737                 }
2738             }
2739             else if (style & WS_SYSMENU )
2740             {
2741                 /* switch to the system menu */
2742                 hNewMenu = get_win_sys_menu( hNewWnd );
2743             }
2744             else return FALSE;
2745         }
2746         else    /* application returned a new menu to switch to */
2747         {
2748             hNewMenu = next_menu.hmenuNext;
2749             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2750
2751             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2752             {
2753                 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2754
2755                 if (style & WS_SYSMENU &&
2756                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2757                 {
2758                     /* get the real system menu */
2759                     hNewMenu =  get_win_sys_menu(hNewWnd);
2760                 }
2761                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2762                 {
2763                     /* FIXME: Not sure what to do here;
2764                      * perhaps try to track hNewMenu as a popup? */
2765
2766                     TRACE(" -- got confused.\n");
2767                     return FALSE;
2768                 }
2769             }
2770             else return FALSE;
2771         }
2772
2773         if( hNewMenu != pmt->hTopMenu )
2774         {
2775             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2776                     FALSE, 0 );
2777             if( pmt->hCurrentMenu != pmt->hTopMenu )
2778                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2779         }
2780
2781         if( hNewWnd != pmt->hOwnerWnd )
2782         {
2783             pmt->hOwnerWnd = hNewWnd;
2784             MENU_SetCapture( pmt->hOwnerWnd );
2785         }
2786
2787         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2788         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2789
2790         return TRUE;
2791     }
2792     return FALSE;
2793 }
2794
2795 /***********************************************************************
2796  *           MENU_SuspendPopup
2797  *
2798  * The idea is not to show the popup if the next input message is
2799  * going to hide it anyway.
2800  */
2801 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2802 {
2803     MSG msg;
2804
2805     msg.hwnd = pmt->hOwnerWnd;
2806
2807     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2808     pmt->trackFlags |= TF_SKIPREMOVE;
2809
2810     switch( uMsg )
2811     {
2812         case WM_KEYDOWN:
2813              PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2814              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2815              {
2816                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2817                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2818                  if( msg.message == WM_KEYDOWN &&
2819                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2820                  {
2821                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2822                      return TRUE;
2823                  }
2824              }
2825              break;
2826     }
2827
2828     /* failures go through this */
2829     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2830     return FALSE;
2831 }
2832
2833 /***********************************************************************
2834  *           MENU_KeyEscape
2835  *
2836  * Handle a VK_ESCAPE key event in a menu.
2837  */
2838 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2839 {
2840     BOOL bEndMenu = TRUE;
2841
2842     if (pmt->hCurrentMenu != pmt->hTopMenu)
2843     {
2844         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2845
2846         if (menu->wFlags & MF_POPUP)
2847         {
2848             HMENU hmenutmp, hmenuprev;
2849
2850             hmenuprev = hmenutmp = pmt->hTopMenu;
2851
2852             /* close topmost popup */
2853             while (hmenutmp != pmt->hCurrentMenu)
2854             {
2855                 hmenuprev = hmenutmp;
2856                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2857             }
2858
2859             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2860             pmt->hCurrentMenu = hmenuprev;
2861             bEndMenu = FALSE;
2862         }
2863     }
2864
2865     return bEndMenu;
2866 }
2867
2868 /***********************************************************************
2869  *           MENU_KeyLeft
2870  *
2871  * Handle a VK_LEFT key event in a menu.
2872  */
2873 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2874 {
2875     POPUPMENU *menu;
2876     HMENU hmenutmp, hmenuprev;
2877     UINT  prevcol;
2878
2879     hmenuprev = hmenutmp = pmt->hTopMenu;
2880     menu = MENU_GetMenu( hmenutmp );
2881
2882     /* Try to move 1 column left (if possible) */
2883     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2884         NO_SELECTED_ITEM ) {
2885
2886         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2887                          prevcol, TRUE, 0 );
2888         return;
2889     }
2890
2891     /* close topmost popup */
2892     while (hmenutmp != pmt->hCurrentMenu)
2893     {
2894         hmenuprev = hmenutmp;
2895         hmenutmp = MENU_GetSubPopup( hmenuprev );
2896     }
2897
2898     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2899     pmt->hCurrentMenu = hmenuprev;
2900
2901     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2902     {
2903         /* move menu bar selection if no more popups are left */
2904
2905         if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2906              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2907
2908         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2909         {
2910            /* A sublevel menu was displayed - display the next one
2911             * unless there is another displacement coming up */
2912
2913             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2914                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2915                                                 pmt->hTopMenu, TRUE, wFlags);
2916         }
2917     }
2918 }
2919
2920
2921 /***********************************************************************
2922  *           MENU_KeyRight
2923  *
2924  * Handle a VK_RIGHT key event in a menu.
2925  */
2926 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2927 {
2928     HMENU hmenutmp;
2929     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2930     UINT  nextcol;
2931
2932     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2933           pmt->hCurrentMenu,
2934           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2935           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2936
2937     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2938     {
2939         /* If already displaying a popup, try to display sub-popup */
2940
2941         hmenutmp = pmt->hCurrentMenu;
2942         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2943
2944         /* if subpopup was displayed then we are done */
2945         if (hmenutmp != pmt->hCurrentMenu) return;
2946     }
2947
2948     /* Check to see if there's another column */
2949     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2950         NO_SELECTED_ITEM ) {
2951         TRACE("Going to %d.\n", nextcol );
2952         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2953                          nextcol, TRUE, 0 );
2954         return;
2955     }
2956
2957     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2958     {
2959         if( pmt->hCurrentMenu != pmt->hTopMenu )
2960         {
2961             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2962             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2963         } else hmenutmp = 0;
2964
2965         /* try to move to the next item */
2966         if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2967              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2968
2969         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2970             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2971                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2972                                                        pmt->hTopMenu, TRUE, wFlags);
2973     }
2974 }
2975
2976 /***********************************************************************
2977  *           MENU_TrackMenu
2978  *
2979  * Menu tracking code.
2980  */
2981 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2982                             HWND hwnd, const RECT *lprect )
2983 {
2984     MSG msg;
2985     POPUPMENU *menu;
2986     BOOL fRemove;
2987     INT executedMenuId = -1;
2988     MTRACKER mt;
2989     BOOL enterIdleSent = FALSE;
2990
2991     mt.trackFlags = 0;
2992     mt.hCurrentMenu = hmenu;
2993     mt.hTopMenu = hmenu;
2994     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2995     mt.pt.x = x;
2996     mt.pt.y = y;
2997
2998     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2999           hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3000
3001     fEndMenu = FALSE;
3002     if (!(menu = MENU_GetMenu( hmenu )))
3003     {
3004         WARN("Invalid menu handle %p\n", hmenu);
3005         SetLastError(ERROR_INVALID_MENU_HANDLE);
3006         return FALSE;
3007     }
3008
3009     if (wFlags & TPM_BUTTONDOWN)
3010     {
3011         /* Get the result in order to start the tracking or not */
3012         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3013         fEndMenu = !fRemove;
3014     }
3015
3016     if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3017
3018     MENU_SetCapture( mt.hOwnerWnd );
3019
3020     while (!fEndMenu)
3021     {
3022         menu = MENU_GetMenu( mt.hCurrentMenu );
3023         if (!menu) /* sometimes happens if I do a window manager close */
3024             break;
3025
3026         /* we have to keep the message in the queue until it's
3027          * clear that menu loop is not over yet. */
3028
3029         for (;;)
3030         {
3031             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3032             {
3033                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3034                 /* remove the message from the queue */
3035                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3036             }
3037             else
3038             {
3039                 if (!enterIdleSent)
3040                 {
3041                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3042                     enterIdleSent = TRUE;
3043                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3044                 }
3045                 WaitMessage();
3046             }
3047         }
3048
3049         /* check if EndMenu() tried to cancel us, by posting this message */
3050         if(msg.message == WM_CANCELMODE)
3051         {
3052             /* we are now out of the loop */
3053             fEndMenu = TRUE;
3054
3055             /* remove the message from the queue */
3056             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3057
3058             /* break out of internal loop, ala ESCAPE */
3059             break;
3060         }
3061
3062         TranslateMessage( &msg );
3063         mt.pt = msg.pt;
3064
3065         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3066           enterIdleSent=FALSE;
3067
3068         fRemove = FALSE;
3069         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3070         {
3071             /*
3072              * Use the mouse coordinates in lParam instead of those in the MSG
3073              * struct to properly handle synthetic messages. They are already
3074              * in screen coordinates.
3075              */
3076             mt.pt.x = (short)LOWORD(msg.lParam);
3077             mt.pt.y = (short)HIWORD(msg.lParam);
3078
3079             /* Find a menu for this mouse event */
3080             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3081
3082             switch(msg.message)
3083             {
3084                 /* no WM_NC... messages in captured state */
3085
3086                 case WM_RBUTTONDBLCLK:
3087                 case WM_RBUTTONDOWN:
3088                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3089                     /* fall through */
3090                 case WM_LBUTTONDBLCLK:
3091                 case WM_LBUTTONDOWN:
3092                     /* If the message belongs to the menu, removes it from the queue */
3093                     /* Else, end menu tracking */
3094                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3095                     fEndMenu = !fRemove;
3096                     break;
3097
3098                 case WM_RBUTTONUP:
3099                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3100                     /* fall through */
3101                 case WM_LBUTTONUP:
3102                     /* Check if a menu was selected by the mouse */
3103                     if (hmenu)
3104                     {
3105                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3106                         TRACE("executedMenuId %d\n", executedMenuId);
3107
3108                         /* End the loop if executedMenuId is an item ID */
3109                         /* or if the job was done (executedMenuId = 0). */
3110                         fEndMenu = fRemove = (executedMenuId != -1);
3111                     }
3112                     /* No menu was selected by the mouse */
3113                     /* if the function was called by TrackPopupMenu, continue
3114                        with the menu tracking. If not, stop it */
3115                     else
3116                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3117
3118                     break;
3119
3120                 case WM_MOUSEMOVE:
3121                     /* the selected menu item must be changed every time */
3122                      /* the mouse moves. */
3123
3124                     if (hmenu)
3125                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3126
3127             } /* switch(msg.message) - mouse */
3128         }
3129         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3130         {
3131             fRemove = TRUE;  /* Keyboard messages are always removed */
3132             switch(msg.message)
3133             {
3134             case WM_KEYDOWN:
3135             case WM_SYSKEYDOWN:
3136                 switch(msg.wParam)
3137                 {
3138                 case VK_MENU:
3139                     fEndMenu = TRUE;
3140                     break;
3141
3142                 case VK_HOME:
3143                 case VK_END:
3144                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3145                                      NO_SELECTED_ITEM, FALSE, 0 );
3146                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3147                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3148                     break;
3149
3150                 case VK_UP:
3151                 case VK_DOWN: /* If on menu bar, pull-down the menu */
3152
3153                     menu = MENU_GetMenu( mt.hCurrentMenu );
3154                     if (!(menu->wFlags & MF_POPUP))
3155                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3156                     else      /* otherwise try to move selection */
3157                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
3158                                        (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3159                     break;
3160
3161                 case VK_LEFT:
3162                     MENU_KeyLeft( &mt, wFlags );
3163                     break;
3164
3165                 case VK_RIGHT:
3166                     MENU_KeyRight( &mt, wFlags );
3167                     break;
3168
3169                 case VK_ESCAPE:
3170                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
3171                     break;
3172
3173                 case VK_F1:
3174                     {
3175                         HELPINFO hi;
3176                         hi.cbSize = sizeof(HELPINFO);
3177                         hi.iContextType = HELPINFO_MENUITEM;
3178                         if (menu->FocusedItem == NO_SELECTED_ITEM)
3179                             hi.iCtrlId = 0;
3180                         else
3181                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3182                         hi.hItemHandle = hmenu;
3183                         hi.dwContextId = menu->dwContextHelpID;
3184                         hi.MousePos = msg.pt;
3185                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3186                         break;
3187                     }
3188
3189                 default:
3190                     break;
3191                 }
3192                 break;  /* WM_KEYDOWN */
3193
3194             case WM_CHAR:
3195             case WM_SYSCHAR:
3196                 {
3197                     UINT        pos;
3198
3199                     if (msg.wParam == '\r' || msg.wParam == ' ')
3200                     {
3201                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3202                         fEndMenu = (executedMenuId != -2);
3203
3204                         break;
3205                     }
3206
3207                       /* Hack to avoid control chars. */
3208                       /* We will find a better way real soon... */
3209                     if (msg.wParam < 32) break;
3210
3211                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3212                                               LOWORD(msg.wParam), FALSE );
3213                     if (pos == (UINT)-2) fEndMenu = TRUE;
3214                     else if (pos == (UINT)-1) MessageBeep(0);
3215                     else
3216                     {
3217                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3218                                 TRUE, 0 );
3219                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3220                         fEndMenu = (executedMenuId != -2);
3221                     }
3222                 }
3223                 break;
3224             }  /* switch(msg.message) - kbd */
3225         }
3226         else
3227         {
3228             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3229             DispatchMessageW( &msg );
3230             continue;
3231         }
3232
3233         if (!fEndMenu) fRemove = TRUE;
3234
3235         /* finally remove message from the queue */
3236
3237         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3238             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3239         else mt.trackFlags &= ~TF_SKIPREMOVE;
3240     }
3241
3242     MENU_SetCapture(0);  /* release the capture */
3243
3244     /* If dropdown is still painted and the close box is clicked on
3245        then the menu will be destroyed as part of the DispatchMessage above.
3246        This will then invalidate the menu handle in mt.hTopMenu. We should
3247        check for this first.  */
3248     if( IsMenu( mt.hTopMenu ) )
3249     {
3250         menu = MENU_GetMenu( mt.hTopMenu );
3251
3252         if( IsWindow( mt.hOwnerWnd ) )
3253         {
3254             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3255
3256             if (menu && (menu->wFlags & MF_POPUP))
3257             {
3258                 DestroyWindow( menu->hWnd );
3259                 menu->hWnd = 0;
3260             }
3261             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3262             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3263         }
3264
3265         /* Reset the variable for hiding menu */
3266         if( menu ) menu->bTimeToHide = FALSE;
3267     }
3268
3269     /* The return value is only used by TrackPopupMenu */
3270     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3271     if (executedMenuId == -1) executedMenuId = 0;
3272     return executedMenuId;
3273 }
3274
3275 /***********************************************************************
3276  *           MENU_InitTracking
3277  */
3278 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3279 {
3280     POPUPMENU *menu;
3281     
3282     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3283
3284     HideCaret(0);
3285
3286     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3287     if (!(wFlags & TPM_NONOTIFY))
3288        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3289
3290     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3291
3292     if (!(wFlags & TPM_NONOTIFY))
3293     {
3294        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3295        /* If an app changed/recreated menu bar entries in WM_INITMENU
3296         * menu sizes will be recalculated once the menu created/shown.
3297         */
3298     }
3299     
3300     /* This makes the menus of applications built with Delphi work.
3301      * It also enables menus to be displayed in more than one window,
3302      * but there are some bugs left that need to be fixed in this case.
3303      */
3304     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3305     
3306     return TRUE;
3307 }
3308 /***********************************************************************
3309  *           MENU_ExitTracking
3310  */
3311 static BOOL MENU_ExitTracking(HWND hWnd)
3312 {
3313     TRACE("hwnd=%p\n", hWnd);
3314
3315     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3316     ShowCaret(0);
3317     top_popup = 0;
3318     return TRUE;
3319 }
3320
3321 /***********************************************************************
3322  *           MENU_TrackMouseMenuBar
3323  *
3324  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3325  */
3326 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3327 {
3328     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3329     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3330
3331     TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3332
3333     if (IsMenu(hMenu))
3334     {
3335         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3336         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3337         MENU_ExitTracking(hWnd);
3338     }
3339 }
3340
3341
3342 /***********************************************************************
3343  *           MENU_TrackKbdMenuBar
3344  *
3345  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3346  */
3347 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3348 {
3349     UINT uItem = NO_SELECTED_ITEM;
3350     HMENU hTrackMenu;
3351     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3352
3353     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3354
3355     /* find window that has a menu */
3356
3357     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3358         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3359
3360     /* check if we have to track a system menu */
3361
3362     hTrackMenu = GetMenu( hwnd );
3363     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3364     {
3365         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3366         hTrackMenu = get_win_sys_menu( hwnd );
3367         uItem = 0;
3368         wParam |= HTSYSMENU; /* prevent item lookup */
3369     }
3370
3371     if (!IsMenu( hTrackMenu )) return;
3372
3373     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3374
3375     if( wChar && wChar != ' ' )
3376     {
3377         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3378         if ( uItem >= (UINT)(-2) )
3379         {
3380             if( uItem == (UINT)(-1) ) MessageBeep(0);
3381             /* schedule end of menu tracking */
3382             wFlags |= TF_ENDMENU;
3383             goto track_menu;
3384         }
3385     }
3386
3387     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3388
3389     if (wParam & HTSYSMENU && wChar != ' ')
3390     {
3391         /* prevent sysmenu activation for managed windows on Alt down/up */
3392         if (GetPropA( hwnd, "__wine_x11_managed" ))
3393             wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3394     }
3395     else
3396     {
3397         if( uItem == NO_SELECTED_ITEM )
3398             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3399         else
3400             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3401     }
3402
3403 track_menu:
3404     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3405     MENU_ExitTracking( hwnd );
3406 }
3407
3408
3409 /**********************************************************************
3410  *           TrackPopupMenu   (USER32.@)
3411  *
3412  * Like the win32 API, the function return the command ID only if the
3413  * flag TPM_RETURNCMD is on.
3414  *
3415  */
3416 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3417                            INT nReserved, HWND hWnd, const RECT *lpRect )
3418 {
3419     BOOL ret = FALSE;
3420
3421     TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3422            hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3423
3424     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3425
3426     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3427     if (!(wFlags & TPM_NONOTIFY))
3428         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3429
3430     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3431         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3432     MENU_ExitTracking(hWnd);
3433
3434     return ret;
3435 }
3436
3437 /**********************************************************************
3438  *           TrackPopupMenuEx   (USER32.@)
3439  */
3440 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3441                                 HWND hWnd, LPTPMPARAMS lpTpm )
3442 {
3443     FIXME("not fully implemented\n" );
3444     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3445                              lpTpm ? &lpTpm->rcExclude : NULL );
3446 }
3447
3448 /***********************************************************************
3449  *           PopupMenuWndProc
3450  *
3451  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3452  */
3453 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3454 {
3455     TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3456
3457     switch(message)
3458     {
3459     case WM_CREATE:
3460         {
3461             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3462             SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3463             return 0;
3464         }
3465
3466     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3467         return MA_NOACTIVATE;
3468
3469     case WM_PAINT:
3470         {
3471             PAINTSTRUCT ps;
3472             BeginPaint( hwnd, &ps );
3473             MENU_DrawPopupMenu( hwnd, ps.hdc,
3474                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3475             EndPaint( hwnd, &ps );
3476             return 0;
3477         }
3478     case WM_ERASEBKGND:
3479         return 1;
3480
3481     case WM_DESTROY:
3482         /* zero out global pointer in case resident popup window was destroyed. */
3483         if (hwnd == top_popup) top_popup = 0;
3484         break;
3485
3486     case WM_SHOWWINDOW:
3487
3488         if( wParam )
3489         {
3490             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3491         }
3492         else
3493             SetWindowLongPtrW( hwnd, 0, 0 );
3494         break;
3495
3496     case MM_SETMENUHANDLE:
3497         SetWindowLongPtrW( hwnd, 0, wParam );
3498         break;
3499
3500     case MM_GETMENUHANDLE:
3501         return GetWindowLongPtrW( hwnd, 0 );
3502
3503     default:
3504         return DefWindowProcW( hwnd, message, wParam, lParam );
3505     }
3506     return 0;
3507 }
3508
3509
3510 /***********************************************************************
3511  *           MENU_GetMenuBarHeight
3512  *
3513  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3514  */
3515 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3516                               INT orgX, INT orgY )
3517 {
3518     HDC hdc;
3519     RECT rectBar;
3520     LPPOPUPMENU lppop;
3521
3522     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3523
3524     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3525
3526     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3527     SelectObject( hdc, get_menu_font(FALSE));
3528     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3529     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3530     ReleaseDC( hwnd, hdc );
3531     return lppop->Height;
3532 }
3533
3534
3535 /*******************************************************************
3536  *         ChangeMenuA    (USER32.@)
3537  */
3538 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3539                              UINT id, UINT flags )
3540 {
3541     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3542     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3543                                                  id, data );
3544     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3545     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3546                                                 id, data );
3547     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3548                                               flags & MF_BYPOSITION ? pos : id,
3549                                               flags & ~MF_REMOVE );
3550     /* Default: MF_INSERT */
3551     return InsertMenuA( hMenu, pos, flags, id, data );
3552 }
3553
3554
3555 /*******************************************************************
3556  *         ChangeMenuW    (USER32.@)
3557  */
3558 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3559                              UINT id, UINT flags )
3560 {
3561     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3562     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3563                                                  id, data );
3564     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3565     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3566                                                 id, data );
3567     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3568                                               flags & MF_BYPOSITION ? pos : id,
3569                                               flags & ~MF_REMOVE );
3570     /* Default: MF_INSERT */
3571     return InsertMenuW( hMenu, pos, flags, id, data );
3572 }
3573
3574
3575 /*******************************************************************
3576  *         CheckMenuItem    (USER32.@)
3577  */
3578 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3579 {
3580     MENUITEM *item;
3581     DWORD ret;
3582
3583     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3584     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3585     ret = item->fState & MF_CHECKED;
3586     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3587     else item->fState &= ~MF_CHECKED;
3588     return ret;
3589 }
3590
3591
3592 /**********************************************************************
3593  *         EnableMenuItem    (USER32.@)
3594  */
3595 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3596 {
3597     UINT    oldflags;
3598     MENUITEM *item;
3599     POPUPMENU *menu;
3600
3601     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3602
3603     /* Get the Popupmenu to access the owner menu */
3604     if (!(menu = MENU_GetMenu(hMenu)))
3605         return (UINT)-1;
3606
3607     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3608         return (UINT)-1;
3609
3610     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3611     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3612
3613     /* If the close item in the system menu change update the close button */
3614     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3615     {
3616         if (menu->hSysMenuOwner != 0)
3617         {
3618             RECT rc;
3619             POPUPMENU* parentMenu;
3620
3621             /* Get the parent menu to access*/
3622             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3623                 return (UINT)-1;
3624
3625             /* Refresh the frame to reflect the change */
3626             GetWindowRect(parentMenu->hWnd, &rc);
3627             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3628             rc.bottom = 0;
3629             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3630         }
3631     }
3632
3633     return oldflags;
3634 }
3635
3636
3637 /*******************************************************************
3638  *         GetMenuStringA    (USER32.@)
3639  */
3640 INT WINAPI GetMenuStringA(
3641         HMENU hMenu,    /* [in] menuhandle */
3642         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3643         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3644         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3645         UINT wFlags     /* [in] MF_ flags */
3646 ) {
3647     MENUITEM *item;
3648
3649     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3650     if (str && nMaxSiz) str[0] = '\0';
3651     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3652         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3653         return 0;
3654     }
3655     if (!item->text) return 0;
3656     if (!str || !nMaxSiz) return strlenW(item->text);
3657     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3658         str[nMaxSiz-1] = 0;
3659     TRACE("returning %s\n", debugstr_a(str));
3660     return strlen(str);
3661 }
3662
3663
3664 /*******************************************************************
3665  *         GetMenuStringW    (USER32.@)
3666  */
3667 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3668                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3669 {
3670     MENUITEM *item;
3671
3672     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3673     if (str && nMaxSiz) str[0] = '\0';
3674     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3675         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3676         return 0;
3677     }
3678     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3679     if( !(item->text)) {
3680         str[0] = 0;
3681         return 0;
3682     }
3683     lstrcpynW( str, item->text, nMaxSiz );
3684     TRACE("returning %s\n", debugstr_w(str));
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 }