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