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