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