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