user: Only include winproc.h where necessary.
[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 )
1800             x -= width - xanchor;
1801         if( x + width > GetSystemMetrics(SM_CXSCREEN))
1802             x = GetSystemMetrics(SM_CXSCREEN) - width;
1803     }
1804     if( x < 0 ) x = 0;
1805
1806     if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1807     {
1808         if( yanchor )
1809             y -= height + yanchor;
1810         if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1811             y = GetSystemMetrics(SM_CYSCREEN) - height;
1812     }
1813     if( y < 0 ) y = 0;
1814
1815     /* NOTE: In Windows, top menu popup is not owned. */
1816     menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1817                                 WS_POPUP, x, y, width, height,
1818                                 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1819                                 (LPVOID)hmenu );
1820     if( !menu->hWnd ) return FALSE;
1821     if (!top_popup) top_popup = menu->hWnd;
1822
1823     /* Display the window */
1824
1825     SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1826                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1827     UpdateWindow( menu->hWnd );
1828     return TRUE;
1829 }
1830
1831
1832 /***********************************************************************
1833  *           MENU_EnsureMenuItemVisible
1834  */
1835 static void
1836 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1837 {
1838     if (lppop->bScrolling)
1839     {
1840         MENUITEM *item = &lppop->items[wIndex];
1841         UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1842         UINT nOldPos = lppop->nScrollPos;
1843         RECT rc;
1844         UINT arrow_bitmap_height;
1845         BITMAP bmp;
1846         
1847         GetClientRect(lppop->hWnd, &rc);
1848
1849         GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1850         arrow_bitmap_height = bmp.bmHeight;
1851
1852         rc.top += arrow_bitmap_height;
1853         rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1854        
1855         nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1856         if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1857         {
1858             
1859             lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1860             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1861             MENU_DrawScrollArrows(lppop, hdc);
1862         }
1863         else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1864         {
1865             lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1866             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1867             MENU_DrawScrollArrows(lppop, hdc);
1868         }
1869     }
1870 }
1871
1872
1873 /***********************************************************************
1874  *           MENU_SelectItem
1875  */
1876 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1877                              BOOL sendMenuSelect, HMENU topmenu )
1878 {
1879     LPPOPUPMENU lppop;
1880     HDC hdc;
1881
1882     TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1883
1884     lppop = MENU_GetMenu( hmenu );
1885     if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1886
1887     if (lppop->FocusedItem == wIndex) return;
1888     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1889     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1890     if (!top_popup) top_popup = lppop->hWnd;
1891
1892     SelectObject( hdc, get_menu_font(FALSE));
1893
1894       /* Clear previous highlighted item */
1895     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1896     {
1897         lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1898         MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1899                           lppop->Height, !(lppop->wFlags & MF_POPUP),
1900                           ODA_SELECT );
1901     }
1902
1903       /* Highlight new item (if any) */
1904     lppop->FocusedItem = wIndex;
1905     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1906     {
1907         if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1908             lppop->items[wIndex].fState |= MF_HILITE;
1909             MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1910             MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1911                     &lppop->items[wIndex], lppop->Height,
1912                     !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1913         }
1914         if (sendMenuSelect)
1915         {
1916             MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1917             SendMessageW( hwndOwner, WM_MENUSELECT,
1918                      MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1919                      ip->fType | ip->fState |
1920                      (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1921         }
1922     }
1923     else if (sendMenuSelect) {
1924         if(topmenu){
1925             int pos;
1926             if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1927                 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1928                 MENUITEM *ip = &ptm->items[pos];
1929                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1930                          ip->fType | ip->fState |
1931                          (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1932             }
1933         }
1934     }
1935     ReleaseDC( lppop->hWnd, hdc );
1936 }
1937
1938
1939 /***********************************************************************
1940  *           MENU_MoveSelection
1941  *
1942  * Moves currently selected item according to the offset parameter.
1943  * If there is no selection then it should select the last item if
1944  * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1945  */
1946 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1947 {
1948     INT i;
1949     POPUPMENU *menu;
1950
1951     TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1952
1953     menu = MENU_GetMenu( hmenu );
1954     if ((!menu) || (!menu->items)) return;
1955
1956     if ( menu->FocusedItem != NO_SELECTED_ITEM )
1957     {
1958         if( menu->nItems == 1 ) return; else
1959         for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1960                                             ; i += offset)
1961             if (!(menu->items[i].fType & MF_SEPARATOR))
1962             {
1963                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1964                 return;
1965             }
1966     }
1967
1968     for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1969                   i >= 0 && i < menu->nItems ; i += offset)
1970         if (!(menu->items[i].fType & MF_SEPARATOR))
1971         {
1972             MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1973             return;
1974         }
1975 }
1976
1977
1978 /**********************************************************************
1979  *         MENU_SetItemData
1980  *
1981  * Set an item's flags, id and text ptr. Called by InsertMenu() and
1982  * ModifyMenu().
1983  */
1984 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1985                                 LPCWSTR str )
1986 {
1987     debug_print_menuitem("MENU_SetItemData from: ", item, "");
1988     TRACE("flags=%x str=%p\n", flags, str);
1989
1990     if (IS_STRING_ITEM(flags))
1991     {
1992         LPWSTR prevText = item->text;
1993         if (!str)
1994         {
1995             flags |= MF_SEPARATOR;
1996             item->text = NULL;
1997         }
1998         else
1999         {
2000             LPWSTR text;
2001             /* Item beginning with a backspace is a help item */
2002             if (*str == '\b')
2003             {
2004                 flags |= MF_HELP;
2005                 str++;
2006             }
2007             if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2008                 return FALSE;
2009             strcpyW( text, str );
2010             item->text = text;
2011         }
2012         item->hbmpItem = NULL;
2013         HeapFree( GetProcessHeap(), 0, prevText );
2014     }
2015     else if(( flags & MFT_BITMAP)) {
2016         item->hbmpItem = HBITMAP_32(LOWORD(str));
2017         /* setting bitmap clears text */
2018         HeapFree( GetProcessHeap(), 0, item->text );
2019         item->text = NULL;
2020     }
2021
2022     if (flags & MF_OWNERDRAW)
2023         item->dwItemData = (DWORD_PTR)str;
2024     else
2025         item->dwItemData = 0;
2026
2027     if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2028         DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */
2029
2030     if (flags & MF_POPUP)
2031     {
2032         POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2033         if (menu) menu->wFlags |= MF_POPUP;
2034         else
2035         {
2036             item->wID = 0;
2037             item->hSubMenu = 0;
2038             item->fType = 0;
2039             item->fState = 0;
2040             return FALSE;
2041         }
2042     }
2043
2044     item->wID = id;
2045     if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2046
2047     if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2048       flags |= MF_POPUP; /* keep popup */
2049
2050     item->fType = flags & TYPE_MASK;
2051     item->fState = (flags & STATE_MASK) &
2052         ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
2053
2054     /* Don't call SetRectEmpty here! */
2055
2056     debug_print_menuitem("MENU_SetItemData to  : ", item, "");
2057     return TRUE;
2058 }
2059
2060
2061 /**********************************************************************
2062  *         MENU_InsertItem
2063  *
2064  * Insert (allocate) a new item into a menu.
2065  */
2066 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2067 {
2068     MENUITEM *newItems;
2069     POPUPMENU *menu;
2070
2071     if (!(menu = MENU_GetMenu(hMenu)))
2072         return NULL;
2073
2074     /* Find where to insert new item */
2075
2076     if (flags & MF_BYPOSITION) {
2077         if (pos > menu->nItems)
2078             pos = menu->nItems;
2079     } else {
2080         if (!MENU_FindItem( &hMenu, &pos, flags ))
2081             pos = menu->nItems;
2082         else {
2083             if (!(menu = MENU_GetMenu( hMenu )))
2084                 return NULL;
2085         }
2086     }
2087
2088     /* Create new items array */
2089
2090     newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2091     if (!newItems)
2092     {
2093         WARN("allocation failed\n" );
2094         return NULL;
2095     }
2096     if (menu->nItems > 0)
2097     {
2098           /* Copy the old array into the new one */
2099         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2100         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2101                                         (menu->nItems-pos)*sizeof(MENUITEM) );
2102         HeapFree( GetProcessHeap(), 0, menu->items );
2103     }
2104     menu->items = newItems;
2105     menu->nItems++;
2106     memset( &newItems[pos], 0, sizeof(*newItems) );
2107     menu->Height = 0; /* force size recalculate */
2108     return &newItems[pos];
2109 }
2110
2111
2112 /**********************************************************************
2113  *         MENU_ParseResource
2114  *
2115  * Parse a standard menu resource and add items to the menu.
2116  * Return a pointer to the end of the resource.
2117  *
2118  * NOTE: flags is equivalent to the mtOption field
2119  */
2120 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2121 {
2122     WORD flags, id = 0;
2123     LPCSTR str;
2124
2125     do
2126     {
2127         flags = GET_WORD(res);
2128         res += sizeof(WORD);
2129         if (!(flags & MF_POPUP))
2130         {
2131             id = GET_WORD(res);
2132             res += sizeof(WORD);
2133         }
2134         str = res;
2135         if (!unicode) res += strlen(str) + 1;
2136         else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2137         if (flags & MF_POPUP)
2138         {
2139             HMENU hSubMenu = CreatePopupMenu();
2140             if (!hSubMenu) return NULL;
2141             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2142                 return NULL;
2143             if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2144             else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2145         }
2146         else  /* Not a popup */
2147         {
2148             if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2149             else AppendMenuW( hMenu, flags, id,
2150                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2151         }
2152     } while (!(flags & MF_END));
2153     return res;
2154 }
2155
2156
2157 /**********************************************************************
2158  *         MENUEX_ParseResource
2159  *
2160  * Parse an extended menu resource and add items to the menu.
2161  * Return a pointer to the end of the resource.
2162  */
2163 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2164 {
2165     WORD resinfo;
2166     do {
2167         MENUITEMINFOW mii;
2168
2169         mii.cbSize = sizeof(mii);
2170         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2171         mii.fType = GET_DWORD(res);
2172         res += sizeof(DWORD);
2173         mii.fState = GET_DWORD(res);
2174         res += sizeof(DWORD);
2175         mii.wID = GET_DWORD(res);
2176         res += sizeof(DWORD);
2177         resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
2178         res += sizeof(WORD);
2179         /* Align the text on a word boundary.  */
2180         res += (~((UINT_PTR)res - 1)) & 1;
2181         mii.dwTypeData = (LPWSTR) res;
2182         res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2183         /* Align the following fields on a dword boundary.  */
2184         res += (~((UINT_PTR)res - 1)) & 3;
2185
2186         TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2187               mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2188
2189         if (resinfo & 1) {      /* Pop-up? */
2190             /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
2191             res += sizeof(DWORD);
2192             mii.hSubMenu = CreatePopupMenu();
2193             if (!mii.hSubMenu)
2194                 return NULL;
2195             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2196                 DestroyMenu(mii.hSubMenu);
2197                 return NULL;
2198             }
2199             mii.fMask |= MIIM_SUBMENU;
2200             mii.fType |= MF_POPUP;
2201         }
2202         else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2203         {
2204             WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2205                 mii.wID, mii.fType);
2206             mii.fType |= MF_SEPARATOR;
2207         }
2208         InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2209     } while (!(resinfo & MF_END));
2210     return res;
2211 }
2212
2213
2214 /***********************************************************************
2215  *           MENU_GetSubPopup
2216  *
2217  * Return the handle of the selected sub-popup menu (if any).
2218  */
2219 static HMENU MENU_GetSubPopup( HMENU hmenu )
2220 {
2221     POPUPMENU *menu;
2222     MENUITEM *item;
2223
2224     menu = MENU_GetMenu( hmenu );
2225
2226     if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2227
2228     item = &menu->items[menu->FocusedItem];
2229     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2230         return item->hSubMenu;
2231     return 0;
2232 }
2233
2234
2235 /***********************************************************************
2236  *           MENU_HideSubPopups
2237  *
2238  * Hide the sub-popup menus of this menu.
2239  */
2240 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2241                                 BOOL sendMenuSelect )
2242 {
2243     POPUPMENU *menu = MENU_GetMenu( hmenu );
2244
2245     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2246
2247     if (menu && top_popup)
2248     {
2249         HMENU hsubmenu;
2250         POPUPMENU *submenu;
2251         MENUITEM *item;
2252
2253         if (menu->FocusedItem != NO_SELECTED_ITEM)
2254         {
2255             item = &menu->items[menu->FocusedItem];
2256             if (!(item->fType & MF_POPUP) ||
2257                 !(item->fState & MF_MOUSESELECT)) return;
2258             item->fState &= ~MF_MOUSESELECT;
2259             hsubmenu = item->hSubMenu;
2260         } else return;
2261
2262         submenu = MENU_GetMenu( hsubmenu );
2263         MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2264         MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2265         DestroyWindow( submenu->hWnd );
2266         submenu->hWnd = 0;
2267     }
2268 }
2269
2270
2271 /***********************************************************************
2272  *           MENU_ShowSubPopup
2273  *
2274  * Display the sub-menu of the selected item of this menu.
2275  * Return the handle of the submenu, or hmenu if no submenu to display.
2276  */
2277 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2278                                   BOOL selectFirst, UINT wFlags )
2279 {
2280     RECT rect;
2281     POPUPMENU *menu;
2282     MENUITEM *item;
2283     HDC hdc;
2284
2285     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2286
2287     if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2288
2289     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2290
2291     item = &menu->items[menu->FocusedItem];
2292     if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2293         return hmenu;
2294
2295     /* message must be sent before using item,
2296        because nearly everything may be changed by the application ! */
2297
2298     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2299     if (!(wFlags & TPM_NONOTIFY))
2300        SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2301                      MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2302
2303     item = &menu->items[menu->FocusedItem];
2304     rect = item->rect;
2305
2306     /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2307     if (!(item->fState & MF_HILITE))
2308     {
2309         if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2310         else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2311
2312         SelectObject( hdc, get_menu_font(FALSE));
2313
2314         item->fState |= MF_HILITE;
2315         MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2316         ReleaseDC( menu->hWnd, hdc );
2317     }
2318     if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2319       item->rect = rect;
2320
2321     item->fState |= MF_MOUSESELECT;
2322
2323     if (IS_SYSTEM_MENU(menu))
2324     {
2325         MENU_InitSysMenuPopup(item->hSubMenu,
2326                               GetWindowLongW( menu->hWnd, GWL_STYLE ),
2327                               GetClassLongW( menu->hWnd, GCL_STYLE));
2328
2329         NC_GetSysPopupPos( menu->hWnd, &rect );
2330         rect.top = rect.bottom;
2331         rect.right = GetSystemMetrics(SM_CXSIZE);
2332         rect.bottom = GetSystemMetrics(SM_CYSIZE);
2333     }
2334     else
2335     {
2336         GetWindowRect( menu->hWnd, &rect );
2337         if (menu->wFlags & MF_POPUP)
2338         {
2339             RECT rc = item->rect;
2340
2341             MENU_AdjustMenuItemRect(menu, &rc);
2342             rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2343             rect.top += rc.top;
2344             rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2345             rect.bottom = rc.top - rc.bottom;
2346         }
2347         else
2348         {
2349             rect.left += item->rect.left;
2350             rect.top += item->rect.bottom;
2351             rect.right = item->rect.right - item->rect.left;
2352             rect.bottom = item->rect.bottom - item->rect.top;
2353         }
2354     }
2355
2356     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2357                     rect.left, rect.top, rect.right, rect.bottom );
2358     if (selectFirst)
2359         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2360     return item->hSubMenu;
2361 }
2362
2363
2364
2365 /**********************************************************************
2366  *         MENU_IsMenuActive
2367  */
2368 HWND MENU_IsMenuActive(void)
2369 {
2370     return top_popup;
2371 }
2372
2373 /***********************************************************************
2374  *           MENU_PtMenu
2375  *
2376  * Walks menu chain trying to find a menu pt maps to.
2377  */
2378 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2379 {
2380    POPUPMENU *menu = MENU_GetMenu( hMenu );
2381    UINT item = menu->FocusedItem;
2382    HMENU ret;
2383
2384    /* try subpopup first (if any) */
2385    ret = (item != NO_SELECTED_ITEM &&
2386           (menu->items[item].fType & MF_POPUP) &&
2387           (menu->items[item].fState & MF_MOUSESELECT))
2388         ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2389
2390    if (!ret)  /* check the current window (avoiding WM_HITTEST) */
2391    {
2392        INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2393        if( menu->wFlags & MF_POPUP )
2394        {
2395            if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2396        }
2397        else if (ht == HTSYSMENU)
2398            ret = get_win_sys_menu( menu->hWnd );
2399        else if (ht == HTMENU)
2400            ret = GetMenu( menu->hWnd );
2401    }
2402    return ret;
2403 }
2404
2405 /***********************************************************************
2406  *           MENU_ExecFocusedItem
2407  *
2408  * Execute a menu item (for instance when user pressed Enter).
2409  * Return the wID of the executed item. Otherwise, -1 indicating
2410  * that no menu item was executed, -2 if a popup is shown;
2411  * Have to receive the flags for the TrackPopupMenu options to avoid
2412  * sending unwanted message.
2413  *
2414  */
2415 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2416 {
2417     MENUITEM *item;
2418     POPUPMENU *menu = MENU_GetMenu( hMenu );
2419
2420     TRACE("%p hmenu=%p\n", pmt, hMenu);
2421
2422     if (!menu || !menu->nItems ||
2423         (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2424
2425     item = &menu->items[menu->FocusedItem];
2426
2427     TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2428
2429     if (!(item->fType & MF_POPUP))
2430     {
2431         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2432         {
2433             /* If TPM_RETURNCMD is set you return the id, but
2434                do not send a message to the owner */
2435             if(!(wFlags & TPM_RETURNCMD))
2436             {
2437                 if( menu->wFlags & MF_SYSMENU )
2438                     PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2439                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2440                 else
2441                     PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2442             }
2443             return item->wID;
2444         }
2445     }
2446     else
2447     {
2448         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2449         return -2;
2450     }
2451
2452     return -1;
2453 }
2454
2455 /***********************************************************************
2456  *           MENU_SwitchTracking
2457  *
2458  * Helper function for menu navigation routines.
2459  */
2460 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2461 {
2462     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2463     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2464
2465     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2466
2467     if( pmt->hTopMenu != hPtMenu &&
2468         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2469     {
2470         /* both are top level menus (system and menu-bar) */
2471         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2472         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2473         pmt->hTopMenu = hPtMenu;
2474     }
2475     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2476     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2477 }
2478
2479
2480 /***********************************************************************
2481  *           MENU_ButtonDown
2482  *
2483  * Return TRUE if we can go on with menu tracking.
2484  */
2485 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2486 {
2487     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2488
2489     if (hPtMenu)
2490     {
2491         UINT id = 0;
2492         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2493         MENUITEM *item;
2494
2495         if( IS_SYSTEM_MENU(ptmenu) )
2496             item = ptmenu->items;
2497         else
2498             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2499
2500         if( item )
2501         {
2502             if( ptmenu->FocusedItem != id )
2503                 MENU_SwitchTracking( pmt, hPtMenu, id );
2504
2505             /* If the popup menu is not already "popped" */
2506             if(!(item->fState & MF_MOUSESELECT ))
2507             {
2508                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2509             }
2510
2511             return TRUE;
2512         }
2513         /* Else the click was on the menu bar, finish the tracking */
2514     }
2515     return FALSE;
2516 }
2517
2518 /***********************************************************************
2519  *           MENU_ButtonUp
2520  *
2521  * Return the value of MENU_ExecFocusedItem if
2522  * the selected item was not a popup. Else open the popup.
2523  * A -1 return value indicates that we go on with menu tracking.
2524  *
2525  */
2526 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2527 {
2528     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2529
2530     if (hPtMenu)
2531     {
2532         UINT id = 0;
2533         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2534         MENUITEM *item;
2535
2536         if( IS_SYSTEM_MENU(ptmenu) )
2537             item = ptmenu->items;
2538         else
2539             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2540
2541         if( item && (ptmenu->FocusedItem == id ))
2542         {
2543             if( !(item->fType & MF_POPUP) )
2544             {
2545                 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2546                 return (executedMenuId < 0) ? -1 : executedMenuId;
2547             }
2548
2549             /* If we are dealing with the top-level menu            */
2550             /* and this is a click on an already "popped" item:     */
2551             /* Stop the menu tracking and close the opened submenus */
2552             if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2553                 return 0;
2554         }
2555         ptmenu->bTimeToHide = TRUE;
2556     }
2557     return -1;
2558 }
2559
2560
2561 /***********************************************************************
2562  *           MENU_MouseMove
2563  *
2564  * Return TRUE if we can go on with menu tracking.
2565  */
2566 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2567 {
2568     UINT id = NO_SELECTED_ITEM;
2569     POPUPMENU *ptmenu = NULL;
2570
2571     if( hPtMenu )
2572     {
2573         ptmenu = MENU_GetMenu( hPtMenu );
2574         if( IS_SYSTEM_MENU(ptmenu) )
2575             id = 0;
2576         else
2577             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2578     }
2579
2580     if( id == NO_SELECTED_ITEM )
2581     {
2582         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2583                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2584
2585     }
2586     else if( ptmenu->FocusedItem != id )
2587     {
2588             MENU_SwitchTracking( pmt, hPtMenu, id );
2589             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2590     }
2591     return TRUE;
2592 }
2593
2594
2595 /***********************************************************************
2596  *           MENU_SetCapture
2597  */
2598 static void MENU_SetCapture( HWND hwnd )
2599 {
2600     HWND previous = 0;
2601
2602     SERVER_START_REQ( set_capture_window )
2603     {
2604         req->handle = hwnd;
2605         req->flags  = CAPTURE_MENU;
2606         if (!wine_server_call_err( req ))
2607         {
2608             previous = reply->previous;
2609             hwnd = reply->full_handle;
2610         }
2611     }
2612     SERVER_END_REQ;
2613
2614     if (previous && previous != hwnd)
2615         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2616 }
2617
2618
2619 /***********************************************************************
2620  *           MENU_DoNextMenu
2621  *
2622  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2623  */
2624 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2625 {
2626     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2627
2628     if( (vk == VK_LEFT &&  menu->FocusedItem == 0 ) ||
2629         (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2630     {
2631         MDINEXTMENU next_menu;
2632         HMENU hNewMenu;
2633         HWND  hNewWnd;
2634         UINT  id = 0;
2635
2636         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2637         next_menu.hmenuNext = 0;
2638         next_menu.hwndNext = 0;
2639         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2640
2641         TRACE("%p [%p] -> %p [%p]\n",
2642               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2643
2644         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2645         {
2646             DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2647             hNewWnd = pmt->hOwnerWnd;
2648             if( IS_SYSTEM_MENU(menu) )
2649             {
2650                 /* switch to the menu bar */
2651
2652                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2653
2654                 if( vk == VK_LEFT )
2655                 {
2656                     menu = MENU_GetMenu( hNewMenu );
2657                     id = menu->nItems - 1;
2658                 }
2659             }
2660             else if (style & WS_SYSMENU )
2661             {
2662                 /* switch to the system menu */
2663                 hNewMenu = get_win_sys_menu( hNewWnd );
2664             }
2665             else return FALSE;
2666         }
2667         else    /* application returned a new menu to switch to */
2668         {
2669             hNewMenu = next_menu.hmenuNext;
2670             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2671
2672             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2673             {
2674                 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2675
2676                 if (style & WS_SYSMENU &&
2677                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2678                 {
2679                     /* get the real system menu */
2680                     hNewMenu =  get_win_sys_menu(hNewWnd);
2681                 }
2682                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2683                 {
2684                     /* FIXME: Not sure what to do here;
2685                      * perhaps try to track hNewMenu as a popup? */
2686
2687                     TRACE(" -- got confused.\n");
2688                     return FALSE;
2689                 }
2690             }
2691             else return FALSE;
2692         }
2693
2694         if( hNewMenu != pmt->hTopMenu )
2695         {
2696             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2697                     FALSE, 0 );
2698             if( pmt->hCurrentMenu != pmt->hTopMenu )
2699                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2700         }
2701
2702         if( hNewWnd != pmt->hOwnerWnd )
2703         {
2704             pmt->hOwnerWnd = hNewWnd;
2705             MENU_SetCapture( pmt->hOwnerWnd );
2706         }
2707
2708         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2709         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2710
2711         return TRUE;
2712     }
2713     return FALSE;
2714 }
2715
2716 /***********************************************************************
2717  *           MENU_SuspendPopup
2718  *
2719  * The idea is not to show the popup if the next input message is
2720  * going to hide it anyway.
2721  */
2722 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2723 {
2724     MSG msg;
2725
2726     msg.hwnd = pmt->hOwnerWnd;
2727
2728     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2729     pmt->trackFlags |= TF_SKIPREMOVE;
2730
2731     switch( uMsg )
2732     {
2733         case WM_KEYDOWN:
2734              PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2735              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2736              {
2737                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2738                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2739                  if( msg.message == WM_KEYDOWN &&
2740                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2741                  {
2742                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2743                      return TRUE;
2744                  }
2745              }
2746              break;
2747     }
2748
2749     /* failures go through this */
2750     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2751     return FALSE;
2752 }
2753
2754 /***********************************************************************
2755  *           MENU_KeyEscape
2756  *
2757  * Handle a VK_ESCAPE key event in a menu.
2758  */
2759 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2760 {
2761     BOOL bEndMenu = TRUE;
2762
2763     if (pmt->hCurrentMenu != pmt->hTopMenu)
2764     {
2765         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2766
2767         if (menu->wFlags & MF_POPUP)
2768         {
2769             HMENU hmenutmp, hmenuprev;
2770
2771             hmenuprev = hmenutmp = pmt->hTopMenu;
2772
2773             /* close topmost popup */
2774             while (hmenutmp != pmt->hCurrentMenu)
2775             {
2776                 hmenuprev = hmenutmp;
2777                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2778             }
2779
2780             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2781             pmt->hCurrentMenu = hmenuprev;
2782             bEndMenu = FALSE;
2783         }
2784     }
2785
2786     return bEndMenu;
2787 }
2788
2789 /***********************************************************************
2790  *           MENU_KeyLeft
2791  *
2792  * Handle a VK_LEFT key event in a menu.
2793  */
2794 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2795 {
2796     POPUPMENU *menu;
2797     HMENU hmenutmp, hmenuprev;
2798     UINT  prevcol;
2799
2800     hmenuprev = hmenutmp = pmt->hTopMenu;
2801     menu = MENU_GetMenu( hmenutmp );
2802
2803     /* Try to move 1 column left (if possible) */
2804     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2805         NO_SELECTED_ITEM ) {
2806
2807         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2808                          prevcol, TRUE, 0 );
2809         return;
2810     }
2811
2812     /* close topmost popup */
2813     while (hmenutmp != pmt->hCurrentMenu)
2814     {
2815         hmenuprev = hmenutmp;
2816         hmenutmp = MENU_GetSubPopup( hmenuprev );
2817     }
2818
2819     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2820     pmt->hCurrentMenu = hmenuprev;
2821
2822     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2823     {
2824         /* move menu bar selection if no more popups are left */
2825
2826         if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2827              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2828
2829         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2830         {
2831            /* A sublevel menu was displayed - display the next one
2832             * unless there is another displacement coming up */
2833
2834             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2835                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2836                                                 pmt->hTopMenu, TRUE, wFlags);
2837         }
2838     }
2839 }
2840
2841
2842 /***********************************************************************
2843  *           MENU_KeyRight
2844  *
2845  * Handle a VK_RIGHT key event in a menu.
2846  */
2847 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2848 {
2849     HMENU hmenutmp;
2850     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2851     UINT  nextcol;
2852
2853     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2854           pmt->hCurrentMenu,
2855           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2856           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2857
2858     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2859     {
2860         /* If already displaying a popup, try to display sub-popup */
2861
2862         hmenutmp = pmt->hCurrentMenu;
2863         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2864
2865         /* if subpopup was displayed then we are done */
2866         if (hmenutmp != pmt->hCurrentMenu) return;
2867     }
2868
2869     /* Check to see if there's another column */
2870     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2871         NO_SELECTED_ITEM ) {
2872         TRACE("Going to %d.\n", nextcol );
2873         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2874                          nextcol, TRUE, 0 );
2875         return;
2876     }
2877
2878     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2879     {
2880         if( pmt->hCurrentMenu != pmt->hTopMenu )
2881         {
2882             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2883             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2884         } else hmenutmp = 0;
2885
2886         /* try to move to the next item */
2887         if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2888              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2889
2890         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2891             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2892                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2893                                                        pmt->hTopMenu, TRUE, wFlags);
2894     }
2895 }
2896
2897 /***********************************************************************
2898  *           MENU_TrackMenu
2899  *
2900  * Menu tracking code.
2901  */
2902 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2903                             HWND hwnd, const RECT *lprect )
2904 {
2905     MSG msg;
2906     POPUPMENU *menu;
2907     BOOL fRemove;
2908     INT executedMenuId = -1;
2909     MTRACKER mt;
2910     BOOL enterIdleSent = FALSE;
2911
2912     mt.trackFlags = 0;
2913     mt.hCurrentMenu = hmenu;
2914     mt.hTopMenu = hmenu;
2915     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2916     mt.pt.x = x;
2917     mt.pt.y = y;
2918
2919     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2920           hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2921
2922     fEndMenu = FALSE;
2923     if (!(menu = MENU_GetMenu( hmenu )))
2924     {
2925         WARN("Invalid menu handle %p\n", hmenu);
2926         SetLastError(ERROR_INVALID_MENU_HANDLE);
2927         return FALSE;
2928     }
2929
2930     if (wFlags & TPM_BUTTONDOWN)
2931     {
2932         /* Get the result in order to start the tracking or not */
2933         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2934         fEndMenu = !fRemove;
2935     }
2936
2937     if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2938
2939     MENU_SetCapture( mt.hOwnerWnd );
2940
2941     while (!fEndMenu)
2942     {
2943         menu = MENU_GetMenu( mt.hCurrentMenu );
2944         if (!menu) /* sometimes happens if I do a window manager close */
2945             break;
2946
2947         /* we have to keep the message in the queue until it's
2948          * clear that menu loop is not over yet. */
2949
2950         for (;;)
2951         {
2952             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2953             {
2954                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2955                 /* remove the message from the queue */
2956                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2957             }
2958             else
2959             {
2960                 if (!enterIdleSent)
2961                 {
2962                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2963                     enterIdleSent = TRUE;
2964                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2965                 }
2966                 WaitMessage();
2967             }
2968         }
2969
2970         /* check if EndMenu() tried to cancel us, by posting this message */
2971         if(msg.message == WM_CANCELMODE)
2972         {
2973             /* we are now out of the loop */
2974             fEndMenu = TRUE;
2975
2976             /* remove the message from the queue */
2977             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2978
2979             /* break out of internal loop, ala ESCAPE */
2980             break;
2981         }
2982
2983         TranslateMessage( &msg );
2984         mt.pt = msg.pt;
2985
2986         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2987           enterIdleSent=FALSE;
2988
2989         fRemove = FALSE;
2990         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2991         {
2992             /*
2993              * Use the mouse coordinates in lParam instead of those in the MSG
2994              * struct to properly handle synthetic messages. They are already
2995              * in screen coordinates.
2996              */
2997             mt.pt.x = (short)LOWORD(msg.lParam);
2998             mt.pt.y = (short)HIWORD(msg.lParam);
2999
3000             /* Find a menu for this mouse event */
3001             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3002
3003             switch(msg.message)
3004             {
3005                 /* no WM_NC... messages in captured state */
3006
3007                 case WM_RBUTTONDBLCLK:
3008                 case WM_RBUTTONDOWN:
3009                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3010                     /* fall through */
3011                 case WM_LBUTTONDBLCLK:
3012                 case WM_LBUTTONDOWN:
3013                     /* If the message belongs to the menu, removes it from the queue */
3014                     /* Else, end menu tracking */
3015                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3016                     fEndMenu = !fRemove;
3017                     break;
3018
3019                 case WM_RBUTTONUP:
3020                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3021                     /* fall through */
3022                 case WM_LBUTTONUP:
3023                     /* Check if a menu was selected by the mouse */
3024                     if (hmenu)
3025                     {
3026                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3027
3028                         /* End the loop if executedMenuId is an item ID */
3029                         /* or if the job was done (executedMenuId = 0). */
3030                         fEndMenu = fRemove = (executedMenuId != -1);
3031                     }
3032                     /* No menu was selected by the mouse */
3033                     /* if the function was called by TrackPopupMenu, continue
3034                        with the menu tracking. If not, stop it */
3035                     else
3036                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3037
3038                     break;
3039
3040                 case WM_MOUSEMOVE:
3041                     /* the selected menu item must be changed every time */
3042                      /* the mouse moves. */
3043
3044                     if (hmenu)
3045                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3046
3047             } /* switch(msg.message) - mouse */
3048         }
3049         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3050         {
3051             fRemove = TRUE;  /* Keyboard messages are always removed */
3052             switch(msg.message)
3053             {
3054             case WM_KEYDOWN:
3055             case WM_SYSKEYDOWN:
3056                 switch(msg.wParam)
3057                 {
3058                 case VK_MENU:
3059                     fEndMenu = TRUE;
3060                     break;
3061
3062                 case VK_HOME:
3063                 case VK_END:
3064                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3065                                      NO_SELECTED_ITEM, FALSE, 0 );
3066                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3067                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3068                     break;
3069
3070                 case VK_UP:
3071                 case VK_DOWN: /* If on menu bar, pull-down the menu */
3072
3073                     menu = MENU_GetMenu( mt.hCurrentMenu );
3074                     if (!(menu->wFlags & MF_POPUP))
3075                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3076                     else      /* otherwise try to move selection */
3077                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
3078                                        (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3079                     break;
3080
3081                 case VK_LEFT:
3082                     MENU_KeyLeft( &mt, wFlags );
3083                     break;
3084
3085                 case VK_RIGHT:
3086                     MENU_KeyRight( &mt, wFlags );
3087                     break;
3088
3089                 case VK_ESCAPE:
3090                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
3091                     break;
3092
3093                 case VK_F1:
3094                     {
3095                         HELPINFO hi;
3096                         hi.cbSize = sizeof(HELPINFO);
3097                         hi.iContextType = HELPINFO_MENUITEM;
3098                         if (menu->FocusedItem == NO_SELECTED_ITEM)
3099                             hi.iCtrlId = 0;
3100                         else
3101                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3102                         hi.hItemHandle = hmenu;
3103                         hi.dwContextId = menu->dwContextHelpID;
3104                         hi.MousePos = msg.pt;
3105                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3106                         break;
3107                     }
3108
3109                 default:
3110                     break;
3111                 }
3112                 break;  /* WM_KEYDOWN */
3113
3114             case WM_CHAR:
3115             case WM_SYSCHAR:
3116                 {
3117                     UINT        pos;
3118
3119                     if (msg.wParam == '\r' || msg.wParam == ' ')
3120                     {
3121                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3122                         fEndMenu = (executedMenuId != -2);
3123
3124                         break;
3125                     }
3126
3127                       /* Hack to avoid control chars. */
3128                       /* We will find a better way real soon... */
3129                     if (msg.wParam < 32) break;
3130
3131                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3132                                               LOWORD(msg.wParam), FALSE );
3133                     if (pos == (UINT)-2) fEndMenu = TRUE;
3134                     else if (pos == (UINT)-1) MessageBeep(0);
3135                     else
3136                     {
3137                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3138                                 TRUE, 0 );
3139                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3140                         fEndMenu = (executedMenuId != -2);
3141                     }
3142                 }
3143                 break;
3144             }  /* switch(msg.message) - kbd */
3145         }
3146         else
3147         {
3148             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3149             DispatchMessageW( &msg );
3150             continue;
3151         }
3152
3153         if (!fEndMenu) fRemove = TRUE;
3154
3155         /* finally remove message from the queue */
3156
3157         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3158             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3159         else mt.trackFlags &= ~TF_SKIPREMOVE;
3160     }
3161
3162     MENU_SetCapture(0);  /* release the capture */
3163
3164     /* If dropdown is still painted and the close box is clicked on
3165        then the menu will be destroyed as part of the DispatchMessage above.
3166        This will then invalidate the menu handle in mt.hTopMenu. We should
3167        check for this first.  */
3168     if( IsMenu( mt.hTopMenu ) )
3169     {
3170         menu = MENU_GetMenu( mt.hTopMenu );
3171
3172         if( IsWindow( mt.hOwnerWnd ) )
3173         {
3174             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3175
3176             if (menu && (menu->wFlags & MF_POPUP))
3177             {
3178                 DestroyWindow( menu->hWnd );
3179                 menu->hWnd = 0;
3180             }
3181             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3182             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3183         }
3184
3185         /* Reset the variable for hiding menu */
3186         if( menu ) menu->bTimeToHide = FALSE;
3187     }
3188
3189     /* The return value is only used by TrackPopupMenu */
3190     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3191     if (executedMenuId < 0) executedMenuId = 0;
3192     return executedMenuId;
3193 }
3194
3195 /***********************************************************************
3196  *           MENU_InitTracking
3197  */
3198 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3199 {
3200     POPUPMENU *menu;
3201     
3202     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3203
3204     HideCaret(0);
3205
3206     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3207     if (!(wFlags & TPM_NONOTIFY))
3208        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3209
3210     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3211
3212     if (!(wFlags & TPM_NONOTIFY))
3213     {
3214        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3215        /* If an app changed/recreated menu bar entries in WM_INITMENU
3216         * menu sizes will be recalculated once the menu created/shown.
3217         */
3218     }
3219     
3220     /* This makes the menus of applications built with Delphi work.
3221      * It also enables menus to be displayed in more than one window,
3222      * but there are some bugs left that need to be fixed in this case.
3223      */
3224     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3225     
3226     return TRUE;
3227 }
3228 /***********************************************************************
3229  *           MENU_ExitTracking
3230  */
3231 static BOOL MENU_ExitTracking(HWND hWnd)
3232 {
3233     TRACE("hwnd=%p\n", hWnd);
3234
3235     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3236     ShowCaret(0);
3237     top_popup = 0;
3238     return TRUE;
3239 }
3240
3241 /***********************************************************************
3242  *           MENU_TrackMouseMenuBar
3243  *
3244  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3245  */
3246 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3247 {
3248     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3249     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3250
3251     TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3252
3253     if (IsMenu(hMenu))
3254     {
3255         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3256         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3257         MENU_ExitTracking(hWnd);
3258     }
3259 }
3260
3261
3262 /***********************************************************************
3263  *           MENU_TrackKbdMenuBar
3264  *
3265  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3266  */
3267 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3268 {
3269     UINT uItem = NO_SELECTED_ITEM;
3270     HMENU hTrackMenu;
3271     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3272
3273     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3274
3275     /* find window that has a menu */
3276
3277     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3278         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3279
3280     /* check if we have to track a system menu */
3281
3282     hTrackMenu = GetMenu( hwnd );
3283     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3284     {
3285         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3286         hTrackMenu = get_win_sys_menu( hwnd );
3287         uItem = 0;
3288         wParam |= HTSYSMENU; /* prevent item lookup */
3289     }
3290
3291     if (!IsMenu( hTrackMenu )) return;
3292
3293     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3294
3295     if( wChar && wChar != ' ' )
3296     {
3297         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3298         if ( uItem >= (UINT)(-2) )
3299         {
3300             if( uItem == (UINT)(-1) ) MessageBeep(0);
3301             /* schedule end of menu tracking */
3302             wFlags |= TF_ENDMENU;
3303             goto track_menu;
3304         }
3305     }
3306
3307     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3308
3309     if (wParam & HTSYSMENU)
3310     {
3311         /* prevent sysmenu activation for managed windows on Alt down/up */
3312         if (GetPropA( hwnd, "__wine_x11_managed" ))
3313             wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3314     }
3315     else
3316     {
3317         if( uItem == NO_SELECTED_ITEM )
3318             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3319         else
3320             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3321     }
3322
3323 track_menu:
3324     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3325     MENU_ExitTracking( hwnd );
3326 }
3327
3328
3329 /**********************************************************************
3330  *           TrackPopupMenu   (USER32.@)
3331  *
3332  * Like the win32 API, the function return the command ID only if the
3333  * flag TPM_RETURNCMD is on.
3334  *
3335  */
3336 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3337                            INT nReserved, HWND hWnd, const RECT *lpRect )
3338 {
3339     BOOL ret = FALSE;
3340
3341     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3342
3343     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3344     if (!(wFlags & TPM_NONOTIFY))
3345         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3346
3347     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3348         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3349     MENU_ExitTracking(hWnd);
3350
3351     return ret;
3352 }
3353
3354 /**********************************************************************
3355  *           TrackPopupMenuEx   (USER32.@)
3356  */
3357 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3358                                 HWND hWnd, LPTPMPARAMS lpTpm )
3359 {
3360     FIXME("not fully implemented\n" );
3361     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3362                              lpTpm ? &lpTpm->rcExclude : NULL );
3363 }
3364
3365 /***********************************************************************
3366  *           PopupMenuWndProc
3367  *
3368  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3369  */
3370 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3371 {
3372     TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3373
3374     switch(message)
3375     {
3376     case WM_CREATE:
3377         {
3378             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3379             SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3380             return 0;
3381         }
3382
3383     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3384         return MA_NOACTIVATE;
3385
3386     case WM_PAINT:
3387         {
3388             PAINTSTRUCT ps;
3389             BeginPaint( hwnd, &ps );
3390             MENU_DrawPopupMenu( hwnd, ps.hdc,
3391                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3392             EndPaint( hwnd, &ps );
3393             return 0;
3394         }
3395     case WM_ERASEBKGND:
3396         return 1;
3397
3398     case WM_DESTROY:
3399         /* zero out global pointer in case resident popup window was destroyed. */
3400         if (hwnd == top_popup) top_popup = 0;
3401         break;
3402
3403     case WM_SHOWWINDOW:
3404
3405         if( wParam )
3406         {
3407             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3408         }
3409         else
3410             SetWindowLongPtrW( hwnd, 0, 0 );
3411         break;
3412
3413     case MM_SETMENUHANDLE:
3414         SetWindowLongPtrW( hwnd, 0, wParam );
3415         break;
3416
3417     case MM_GETMENUHANDLE:
3418         return GetWindowLongPtrW( hwnd, 0 );
3419
3420     default:
3421         return DefWindowProcW( hwnd, message, wParam, lParam );
3422     }
3423     return 0;
3424 }
3425
3426
3427 /***********************************************************************
3428  *           MENU_GetMenuBarHeight
3429  *
3430  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3431  */
3432 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3433                               INT orgX, INT orgY )
3434 {
3435     HDC hdc;
3436     RECT rectBar;
3437     LPPOPUPMENU lppop;
3438
3439     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3440
3441     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3442
3443     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3444     SelectObject( hdc, get_menu_font(FALSE));
3445     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3446     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3447     ReleaseDC( hwnd, hdc );
3448     return lppop->Height;
3449 }
3450
3451
3452 /*******************************************************************
3453  *         ChangeMenuA    (USER32.@)
3454  */
3455 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3456                              UINT id, UINT flags )
3457 {
3458     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3459     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3460                                                  id, data );
3461     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3462     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3463                                                 id, data );
3464     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3465                                               flags & MF_BYPOSITION ? pos : id,
3466                                               flags & ~MF_REMOVE );
3467     /* Default: MF_INSERT */
3468     return InsertMenuA( hMenu, pos, flags, id, data );
3469 }
3470
3471
3472 /*******************************************************************
3473  *         ChangeMenuW    (USER32.@)
3474  */
3475 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3476                              UINT id, UINT flags )
3477 {
3478     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3479     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3480                                                  id, data );
3481     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3482     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3483                                                 id, data );
3484     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3485                                               flags & MF_BYPOSITION ? pos : id,
3486                                               flags & ~MF_REMOVE );
3487     /* Default: MF_INSERT */
3488     return InsertMenuW( hMenu, pos, flags, id, data );
3489 }
3490
3491
3492 /*******************************************************************
3493  *         CheckMenuItem    (USER32.@)
3494  */
3495 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3496 {
3497     MENUITEM *item;
3498     DWORD ret;
3499
3500     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3501     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3502     ret = item->fState & MF_CHECKED;
3503     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3504     else item->fState &= ~MF_CHECKED;
3505     return ret;
3506 }
3507
3508
3509 /**********************************************************************
3510  *         EnableMenuItem    (USER32.@)
3511  */
3512 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3513 {
3514     UINT    oldflags;
3515     MENUITEM *item;
3516     POPUPMENU *menu;
3517
3518     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3519
3520     /* Get the Popupmenu to access the owner menu */
3521     if (!(menu = MENU_GetMenu(hMenu)))
3522         return (UINT)-1;
3523
3524     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3525         return (UINT)-1;
3526
3527     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3528     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3529
3530     /* If the close item in the system menu change update the close button */
3531     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3532     {
3533         if (menu->hSysMenuOwner != 0)
3534         {
3535             RECT rc;
3536             POPUPMENU* parentMenu;
3537
3538             /* Get the parent menu to access*/
3539             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3540                 return (UINT)-1;
3541
3542             /* Refresh the frame to reflect the change */
3543             GetWindowRect(parentMenu->hWnd, &rc);
3544             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3545             rc.bottom = 0;
3546             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3547         }
3548     }
3549
3550     return oldflags;
3551 }
3552
3553
3554 /*******************************************************************
3555  *         GetMenuStringA    (USER32.@)
3556  */
3557 INT WINAPI GetMenuStringA(
3558         HMENU hMenu,    /* [in] menuhandle */
3559         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3560         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3561         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3562         UINT wFlags     /* [in] MF_ flags */
3563 ) {
3564     MENUITEM *item;
3565
3566     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3567     if (str && nMaxSiz) str[0] = '\0';
3568     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3569         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3570         return 0;
3571     }
3572     if (!item->text) return 0;
3573     if (!str || !nMaxSiz) return strlenW(item->text);
3574     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3575         str[nMaxSiz-1] = 0;
3576     TRACE("returning '%s'\n", str );
3577     return strlen(str);
3578 }
3579
3580
3581 /*******************************************************************
3582  *         GetMenuStringW    (USER32.@)
3583  */
3584 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3585                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3586 {
3587     MENUITEM *item;
3588
3589     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3590     if (str && nMaxSiz) str[0] = '\0';
3591     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3592         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3593         return 0;
3594     }
3595     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3596     if( !(item->text)) {
3597         str[0] = 0;
3598         return 0;
3599     }
3600     lstrcpynW( str, item->text, nMaxSiz );
3601     return strlenW(str);
3602 }
3603
3604
3605 /**********************************************************************
3606  *         HiliteMenuItem    (USER32.@)
3607  */
3608 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3609                                 UINT wHilite )
3610 {
3611     LPPOPUPMENU menu;
3612     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3613     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3614     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3615     if (menu->FocusedItem == wItemID) return TRUE;
3616     MENU_HideSubPopups( hWnd, hMenu, FALSE );
3617     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3618     return TRUE;
3619 }
3620
3621
3622 /**********************************************************************
3623  *         GetMenuState    (USER32.@)
3624  */
3625 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3626 {
3627     MENUITEM *item;
3628     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3629     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3630     debug_print_menuitem ("  item: ", item, "");
3631     if (item->fType & MF_POPUP)
3632     {
3633         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3634         if (!menu) return -1;
3635         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3636     }
3637     else
3638     {
3639         /* We used to (from way back then) mask the result to 0xff.  */
3640         /* I don't know why and it seems wrong as the documented */
3641         /* return flag MF_SEPARATOR is outside that mask.  */
3642         return (item->fType | item->fState);
3643     }
3644 }
3645
3646
3647 /**********************************************************************
3648  *         GetMenuItemCount    (USER32.@)
3649  */
3650 INT WINAPI GetMenuItemCount( HMENU hMenu )
3651 {
3652     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3653     if (!menu) return -1;
3654     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3655     return menu->nItems;
3656 }
3657
3658
3659 /**********************************************************************
3660  *         GetMenuItemID    (USER32.@)
3661  */
3662 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3663 {
3664     MENUITEM * lpmi;
3665
3666     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3667     if (lpmi->fType & MF_POPUP) return -1;
3668     return lpmi->wID;
3669
3670 }
3671
3672
3673 /*******************************************************************
3674  *         InsertMenuW    (USER32.@)
3675  */
3676 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3677                          UINT_PTR id, LPCWSTR str )
3678 {
3679     MENUITEM *item;
3680
3681     if (IS_STRING_ITEM(flags) && str)
3682         TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3683               hMenu, pos, flags, id, debugstr_w(str) );
3684     else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3685                hMenu, pos, flags, id, str );
3686
3687     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3688
3689     if (!(MENU_SetItemData( item, flags, id, str )))
3690     {
3691         RemoveMenu( hMenu, pos, flags );
3692         return FALSE;
3693     }
3694
3695     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3696         (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3697
3698     item->hCheckBit = item->hUnCheckBit = 0;
3699     return TRUE;
3700 }
3701
3702
3703 /*******************************************************************
3704  *         InsertMenuA    (USER32.@)
3705  */
3706 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3707                          UINT_PTR id, LPCSTR str )
3708 {
3709     BOOL ret = FALSE;
3710
3711     if (IS_STRING_ITEM(flags) && str)
3712     {
3713         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3714         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3715         if (newstr)
3716         {
3717             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3718             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3719             HeapFree( GetProcessHeap(), 0, newstr );
3720         }
3721         return ret;
3722     }
3723     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3724 }
3725
3726
3727 /*******************************************************************
3728  *         AppendMenuA    (USER32.@)
3729  */
3730 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3731                          UINT_PTR id, LPCSTR data )
3732 {
3733     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3734 }
3735
3736
3737 /*******************************************************************
3738  *         AppendMenuW    (USER32.@)
3739  */
3740 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3741                          UINT_PTR id, LPCWSTR data )
3742 {
3743     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3744 }
3745
3746
3747 /**********************************************************************
3748  *         RemoveMenu    (USER32.@)
3749  */
3750 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3751 {
3752     LPPOPUPMENU menu;
3753     MENUITEM *item;
3754
3755     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3756     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3757     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3758
3759       /* Remove item */
3760
3761     MENU_FreeItemData( item );
3762
3763     if (--menu->nItems == 0)
3764     {
3765         HeapFree( GetProcessHeap(), 0, menu->items );
3766         menu->items = NULL;
3767     }
3768     else
3769     {
3770         while(nPos < menu->nItems)
3771         {
3772             *item = *(item+1);
3773             item++;
3774             nPos++;
3775         }
3776         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3777                                    menu->nItems * sizeof(MENUITEM) );
3778     }
3779     return TRUE;
3780 }
3781
3782
3783 /**********************************************************************
3784  *         DeleteMenu    (USER32.@)
3785  */
3786 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3787 {
3788     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3789     if (!item) return FALSE;
3790     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3791       /* nPos is now the position of the item */
3792     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3793     return TRUE;
3794 }
3795
3796
3797 /*******************************************************************
3798  *         ModifyMenuW    (USER32.@)
3799  */
3800 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3801                          UINT_PTR id, LPCWSTR str )
3802 {
3803     MENUITEM *item;
3804
3805     if (IS_STRING_ITEM(flags))
3806         TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3807     else
3808         TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3809
3810     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3811     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3812     return MENU_SetItemData( item, flags, id, str );
3813 }
3814
3815
3816 /*******************************************************************
3817  *         ModifyMenuA    (USER32.@)
3818  */
3819 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3820                          UINT_PTR id, LPCSTR str )
3821 {
3822     BOOL ret = FALSE;
3823
3824     if (IS_STRING_ITEM(flags) && str)
3825     {
3826         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3827         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3828         if (newstr)
3829         {
3830             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3831             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3832             HeapFree( GetProcessHeap(), 0, newstr );
3833         }
3834         return ret;
3835     }
3836     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3837 }
3838
3839
3840 /**********************************************************************
3841  *         CreatePopupMenu    (USER32.@)
3842  */
3843 HMENU WINAPI CreatePopupMenu(void)
3844 {
3845     HMENU hmenu;
3846     POPUPMENU *menu;
3847
3848     if (!(hmenu = CreateMenu())) return 0;
3849     menu = MENU_GetMenu( hmenu );
3850     menu->wFlags |= MF_POPUP;
3851     menu->bTimeToHide = FALSE;
3852     return hmenu;
3853 }
3854
3855
3856 /**********************************************************************
3857  *         GetMenuCheckMarkDimensions    (USER.417)
3858  *         GetMenuCheckMarkDimensions    (USER32.@)
3859  */
3860 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3861 {
3862     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3863 }
3864
3865
3866 /**********************************************************************
3867  *         SetMenuItemBitmaps    (USER32.@)
3868  */
3869 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3870                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3871 {
3872     MENUITEM *item;
3873     TRACE("(%p, %04x, %04x, %p, %p)\n",
3874           hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3875     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3876
3877     if (!hNewCheck && !hNewUnCheck)
3878     {
3879         item->fState &= ~MF_USECHECKBITMAPS;
3880     }
3881     else  /* Install new bitmaps */
3882     {
3883         item->hCheckBit = hNewCheck;
3884         item->hUnCheckBit = hNewUnCheck;
3885         item->fState |= MF_USECHECKBITMAPS;
3886     }
3887     return TRUE;
3888 }
3889
3890
3891 /**********************************************************************
3892  *         CreateMenu    (USER32.@)
3893  */
3894 HMENU WINAPI CreateMenu(void)
3895 {
3896     HMENU hMenu;
3897     LPPOPUPMENU menu;
3898     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3899     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3900
3901     ZeroMemory(menu, sizeof(POPUPMENU));
3902     menu->wMagic = MENU_MAGIC;
3903     menu->FocusedItem = NO_SELECTED_ITEM;
3904     menu->bTimeToHide = FALSE;
3905
3906     TRACE("return %p\n", hMenu );
3907
3908     return hMenu;
3909 }
3910
3911
3912 /**********************************************************************
3913  *         DestroyMenu    (USER32.@)
3914  */
3915 BOOL WINAPI DestroyMenu( HMENU hMenu )
3916 {
3917     LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3918
3919     TRACE("(%p)\n", hMenu);
3920
3921
3922     if (!lppop) return FALSE;
3923
3924     lppop->wMagic = 0;  /* Mark it as destroyed */
3925
3926     /* DestroyMenu should not destroy system menu popup owner */
3927     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3928     {
3929         DestroyWindow( lppop->hWnd );
3930         lppop->hWnd = 0;
3931     }
3932
3933     if (lppop->items) /* recursively destroy submenus */
3934     {
3935         int i;
3936         MENUITEM *item = lppop->items;
3937         for (i = lppop->nItems; i > 0; i--, item++)
3938         {
3939             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3940             MENU_FreeItemData( item );
3941         }
3942         HeapFree( GetProcessHeap(), 0, lppop->items );
3943     }
3944     USER_HEAP_FREE( hMenu );
3945     return TRUE;
3946 }
3947
3948
3949 /**********************************************************************
3950  *         GetSystemMenu    (USER32.@)
3951  */
3952 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3953 {
3954     WND *wndPtr = WIN_GetPtr( hWnd );
3955     HMENU retvalue = 0;
3956
3957     if (wndPtr == WND_DESKTOP) return 0;
3958     if (wndPtr == WND_OTHER_PROCESS)
3959     {
3960         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3961     }
3962     else if (wndPtr)
3963     {
3964         if (wndPtr->hSysMenu && bRevert)
3965         {
3966             DestroyMenu(wndPtr->hSysMenu);
3967             wndPtr->hSysMenu = 0;
3968         }
3969
3970         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3971             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3972
3973         if( wndPtr->hSysMenu )
3974         {
3975             POPUPMENU *menu;
3976             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3977
3978             /* Store the dummy sysmenu handle to facilitate the refresh */
3979             /* of the close button if the SC_CLOSE item change */
3980             menu = MENU_GetMenu(retvalue);
3981             if ( menu )
3982                menu->hSysMenuOwner = wndPtr->hSysMenu;
3983         }
3984         WIN_ReleasePtr( wndPtr );
3985     }
3986     return bRevert ? 0 : retvalue;
3987 }
3988
3989
3990 /*******************************************************************
3991  *         SetSystemMenu    (USER32.@)
3992  */
3993 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3994 {
3995     WND *wndPtr = WIN_GetPtr( hwnd );
3996
3997     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3998     {
3999         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4000         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4001         WIN_ReleasePtr( wndPtr );
4002         return TRUE;
4003     }
4004     return FALSE;
4005 }
4006
4007
4008 /**********************************************************************
4009  *         GetMenu    (USER32.@)
4010  */
4011 HMENU WINAPI GetMenu( HWND hWnd )
4012 {
4013     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4014     TRACE("for %p returning %p\n", hWnd, retvalue);
4015     return retvalue;
4016 }
4017
4018 /**********************************************************************
4019  *         GetMenuBarInfo    (USER32.@)
4020  */
4021 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4022 {
4023     FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4024     return FALSE;
4025 }
4026
4027 /**********************************************************************
4028  *         MENU_SetMenu
4029  *
4030  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4031  * SetWindowPos call that would result if SetMenu were called directly.
4032  */
4033 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4034 {
4035     TRACE("(%p, %p);\n", hWnd, hMenu);
4036
4037     if (hMenu && !IsMenu(hMenu))
4038     {
4039         WARN("hMenu %p is not a menu handle\n", hMenu);
4040         SetLastError(ERROR_INVALID_MENU_HANDLE);
4041         return FALSE;
4042     }
4043     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4044         return FALSE;
4045
4046     hWnd = WIN_GetFullHandle( hWnd );
4047     if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
4048
4049     if (hMenu != 0)
4050     {
4051         LPPOPUPMENU lpmenu;
4052
4053         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4054
4055         lpmenu->hWnd = hWnd;
4056         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4057     }
4058     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4059     return TRUE;
4060 }
4061
4062
4063 /**********************************************************************
4064  *         SetMenu    (USER32.@)
4065  */
4066 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4067 {   
4068     if(!MENU_SetMenu(hWnd, hMenu))
4069         return FALSE;
4070  
4071     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4072                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4073     return TRUE;
4074 }
4075
4076
4077 /**********************************************************************
4078  *         GetSubMenu    (USER32.@)
4079  */
4080 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4081 {
4082     MENUITEM * lpmi;
4083
4084     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4085     if (!(lpmi->fType & MF_POPUP)) return 0;
4086     return lpmi->hSubMenu;
4087 }
4088
4089
4090 /**********************************************************************
4091  *         DrawMenuBar    (USER32.@)
4092  */
4093 BOOL WINAPI DrawMenuBar( HWND hWnd )
4094 {
4095     LPPOPUPMENU lppop;
4096     HMENU hMenu = GetMenu(hWnd);
4097
4098     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4099         return FALSE;
4100     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4101
4102     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4103     lppop->hwndOwner = hWnd;
4104     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4105                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4106     return TRUE;
4107 }
4108
4109 /***********************************************************************
4110  *           DrawMenuBarTemp   (USER32.@)
4111  *
4112  * UNDOCUMENTED !!
4113  *
4114  * called by W98SE desk.cpl Control Panel Applet
4115  *
4116  * Not 100% sure about the param names, but close.
4117  */
4118 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4119 {
4120     LPPOPUPMENU lppop;
4121     UINT i,retvalue;
4122     HFONT hfontOld = 0;
4123     BOOL flat_menu = FALSE;
4124
4125     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4126
4127     if (!hMenu)
4128         hMenu = GetMenu(hwnd);
4129
4130     if (!hFont)
4131         hFont = get_menu_font(FALSE);
4132
4133     lppop = MENU_GetMenu( hMenu );
4134     if (lppop == NULL || lprect == NULL)
4135     {
4136         retvalue = GetSystemMetrics(SM_CYMENU);
4137         goto END;
4138     }
4139
4140     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4141
4142     hfontOld = SelectObject( hDC, hFont);
4143
4144     if (lppop->Height == 0)
4145         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4146
4147     lprect->bottom = lprect->top + lppop->Height;
4148
4149     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4150
4151     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4152     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4153     LineTo( hDC, lprect->right, lprect->bottom );
4154
4155     if (lppop->nItems == 0)
4156     {
4157         retvalue = GetSystemMetrics(SM_CYMENU);
4158         goto END;
4159     }
4160
4161     for (i = 0; i < lppop->nItems; i++)
4162     {
4163         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4164                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4165     }
4166     retvalue = lppop->Height;
4167
4168 END:
4169     if (hfontOld) SelectObject (hDC, hfontOld);
4170     return retvalue;
4171 }
4172
4173 /***********************************************************************
4174  *           EndMenu   (USER.187)
4175  *           EndMenu   (USER32.@)
4176  */
4177 void WINAPI EndMenu(void)
4178 {
4179     /* if we are in the menu code, and it is active */
4180     if (!fEndMenu && top_popup)
4181     {
4182         /* terminate the menu handling code */
4183         fEndMenu = TRUE;
4184
4185         /* needs to be posted to wakeup the internal menu handler */
4186         /* which will now terminate the menu, in the event that */
4187         /* the main window was minimized, or lost focus, so we */
4188         /* don't end up with an orphaned menu */
4189         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4190     }
4191 }
4192
4193
4194 /***********************************************************************
4195  *           LookupMenuHandle   (USER.217)
4196  */
4197 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4198 {
4199     HMENU hmenu32 = HMENU_32(hmenu);
4200     UINT id32 = id;
4201     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4202     else return HMENU_16(hmenu32);
4203 }
4204
4205
4206 /**********************************************************************
4207  *          LoadMenu    (USER.150)
4208  */
4209 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4210 {
4211     HRSRC16 hRsrc;
4212     HGLOBAL16 handle;
4213     HMENU16 hMenu;
4214
4215     if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4216     if (!name) return 0;
4217
4218     instance = GetExePtr( instance );
4219     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4220     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4221     hMenu = LoadMenuIndirect16(LockResource16(handle));
4222     FreeResource16( handle );
4223     return hMenu;
4224 }
4225
4226
4227 /*****************************************************************
4228  *        LoadMenuA   (USER32.@)
4229  */
4230 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4231 {
4232     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4233     if (!hrsrc) return 0;
4234     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4235 }
4236
4237
4238 /*****************************************************************
4239  *        LoadMenuW   (USER32.@)
4240  */
4241 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4242 {
4243     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4244     if (!hrsrc) return 0;
4245     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4246 }
4247
4248
4249 /**********************************************************************
4250  *          LoadMenuIndirect    (USER.220)
4251  */
4252 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4253 {
4254     HMENU hMenu;
4255     WORD version, offset;
4256     LPCSTR p = (LPCSTR)template;
4257
4258     TRACE("(%p)\n", template );
4259     version = GET_WORD(p);
4260     p += sizeof(WORD);
4261     if (version)
4262     {
4263         WARN("version must be 0 for Win16\n" );
4264         return 0;
4265     }
4266     offset = GET_WORD(p);
4267     p += sizeof(WORD) + offset;
4268     if (!(hMenu = CreateMenu())) return 0;
4269     if (!MENU_ParseResource( p, hMenu, FALSE ))
4270     {
4271         DestroyMenu( hMenu );
4272         return 0;
4273     }
4274     return HMENU_16(hMenu);
4275 }
4276
4277
4278 /**********************************************************************
4279  *          LoadMenuIndirectW    (USER32.@)
4280  */
4281 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4282 {
4283     HMENU hMenu;
4284     WORD version, offset;
4285     LPCSTR p = (LPCSTR)template;
4286
4287     version = GET_WORD(p);
4288     p += sizeof(WORD);
4289     TRACE("%p, ver %d\n", template, version );
4290     switch (version)
4291     {
4292       case 0: /* standard format is version of 0 */
4293         offset = GET_WORD(p);
4294         p += sizeof(WORD) + offset;
4295         if (!(hMenu = CreateMenu())) return 0;
4296         if (!MENU_ParseResource( p, hMenu, TRUE ))
4297           {
4298             DestroyMenu( hMenu );
4299             return 0;
4300           }
4301         return hMenu;
4302       case 1: /* extended format is version of 1 */
4303         offset = GET_WORD(p);
4304         p += sizeof(WORD) + offset;
4305         if (!(hMenu = CreateMenu())) return 0;
4306         if (!MENUEX_ParseResource( p, hMenu))
4307           {
4308             DestroyMenu( hMenu );
4309             return 0;
4310           }
4311         return hMenu;
4312       default:
4313         ERR("version %d not supported.\n", version);
4314         return 0;
4315     }
4316 }
4317
4318
4319 /**********************************************************************
4320  *          LoadMenuIndirectA    (USER32.@)
4321  */
4322 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4323 {
4324     return LoadMenuIndirectW( template );
4325 }
4326
4327
4328 /**********************************************************************
4329  *              IsMenu    (USER32.@)
4330  */
4331 BOOL WINAPI IsMenu(HMENU hmenu)
4332 {
4333     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4334     return menu != NULL;
4335 }
4336
4337 /**********************************************************************
4338  *              GetMenuItemInfo_common
4339  */
4340
4341 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4342                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4343 {
4344     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4345
4346     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4347
4348     if (!menu)
4349         return FALSE;
4350     
4351     if( lpmii->fMask & MIIM_TYPE) {
4352         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4353             WARN("invalid combination of fMask bits used\n");
4354             /* this does not happen on Win9x/ME */
4355             SetLastError( ERROR_INVALID_PARAMETER);
4356             return FALSE;
4357         }
4358         lpmii->fType = menu->fType & ~MF_POPUP;
4359         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4360         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4361         if( lpmii->fType & MFT_BITMAP) {
4362             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4363             lpmii->cch = 0;
4364         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4365             /* this does not happen on Win9x/ME */
4366             lpmii->dwTypeData = 0;
4367             lpmii->cch = 0;
4368         }
4369     }
4370
4371     /* copy the text string */
4372     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4373          if( !menu->text ) {
4374                 if(lpmii->dwTypeData && lpmii->cch) {
4375                     lpmii->cch = 0;
4376                     if( unicode)
4377                         *((WCHAR *)lpmii->dwTypeData) = 0;
4378                     else
4379                         *((CHAR *)lpmii->dwTypeData) = 0;
4380                 }
4381          } else {
4382             int len;
4383             if (unicode)
4384             {
4385                 len = strlenW(menu->text);
4386                 if(lpmii->dwTypeData && lpmii->cch)
4387                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4388             }
4389             else
4390             {
4391                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4392                         0, NULL, NULL ) - 1;
4393                 if(lpmii->dwTypeData && lpmii->cch)
4394                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4395                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4396                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4397             }
4398             /* if we've copied a substring we return its length */
4399             if(lpmii->dwTypeData && lpmii->cch)
4400                 if (lpmii->cch <= len + 1)
4401                     lpmii->cch--;
4402                 else
4403                     lpmii->cch = len;
4404             else {
4405                 /* return length of string */
4406                 /* not on Win9x/ME if fType & MFT_BITMAP */
4407                 lpmii->cch = len;
4408             }
4409         }
4410     }
4411
4412     if (lpmii->fMask & MIIM_FTYPE)
4413         lpmii->fType = menu->fType & ~MF_POPUP;
4414
4415     if (lpmii->fMask & MIIM_BITMAP)
4416         lpmii->hbmpItem = menu->hbmpItem;
4417
4418     if (lpmii->fMask & MIIM_STATE)
4419         lpmii->fState = menu->fState;
4420
4421     if (lpmii->fMask & MIIM_ID)
4422         lpmii->wID = menu->wID;
4423
4424     if (lpmii->fMask & MIIM_SUBMENU)
4425         lpmii->hSubMenu = menu->hSubMenu;
4426     else {
4427         /* hSubMenu is always cleared 
4428          * (not on Win9x/ME ) */
4429         lpmii->hSubMenu = 0;
4430     }
4431
4432     if (lpmii->fMask & MIIM_CHECKMARKS) {
4433         lpmii->hbmpChecked = menu->hCheckBit;
4434         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4435     }
4436     if (lpmii->fMask & MIIM_DATA)
4437         lpmii->dwItemData = menu->dwItemData;
4438
4439   return TRUE;
4440 }
4441
4442 /**********************************************************************
4443  *              GetMenuItemInfoA    (USER32.@)
4444  */
4445 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4446                                   LPMENUITEMINFOA lpmii)
4447 {
4448     BOOL ret;
4449     MENUITEMINFOA mii;
4450     if( lpmii->cbSize != sizeof( mii) &&
4451             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4452         SetLastError( ERROR_INVALID_PARAMETER);
4453         return FALSE;
4454     }
4455     memcpy( &mii, lpmii, lpmii->cbSize);
4456     mii.cbSize = sizeof( mii);
4457     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4458                                     (LPMENUITEMINFOW)&mii, FALSE);
4459     mii.cbSize = lpmii->cbSize;
4460     memcpy( lpmii, &mii, mii.cbSize);
4461     return ret;
4462 }
4463
4464 /**********************************************************************
4465  *              GetMenuItemInfoW    (USER32.@)
4466  */
4467 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4468                                   LPMENUITEMINFOW lpmii)
4469 {
4470     BOOL ret;
4471     MENUITEMINFOW mii;
4472     if( lpmii->cbSize != sizeof( mii) &&
4473             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4474         SetLastError( ERROR_INVALID_PARAMETER);
4475         return FALSE;
4476     }
4477     memcpy( &mii, lpmii, lpmii->cbSize);
4478     mii.cbSize = sizeof( mii);
4479     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4480     mii.cbSize = lpmii->cbSize;
4481     memcpy( lpmii, &mii, mii.cbSize);
4482     return ret;
4483 }
4484
4485
4486 /* set a menu item text from a ASCII or Unicode string */
4487 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4488 {
4489     if (!text)
4490         menu->text = NULL;
4491     else if (unicode)
4492     {
4493         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4494             strcpyW( menu->text, text );
4495     }
4496     else
4497     {
4498         LPCSTR str = (LPCSTR)text;
4499         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4500         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4501             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4502     }
4503 }
4504
4505
4506 /**********************************************************************
4507  *              SetMenuItemInfo_common
4508  */
4509
4510 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4511                                        const MENUITEMINFOW *lpmii,
4512                                        BOOL unicode)
4513 {
4514     if (!menu) return FALSE;
4515
4516     debug_print_menuitem("SetmenuItemInfo_common from: ", menu, "");
4517
4518     if (lpmii->fMask & MIIM_TYPE ) {
4519         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4520             WARN("invalid combination of fMask bits used\n");
4521             /* this does not happen on Win9x/ME */
4522             SetLastError( ERROR_INVALID_PARAMETER);
4523             return FALSE;
4524         }
4525         /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4526         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4527         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4528
4529         if (IS_STRING_ITEM(menu->fType)) {
4530             HeapFree(GetProcessHeap(), 0, menu->text);
4531             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4532         } else if( (menu->fType) & MFT_BITMAP)
4533                 menu->hbmpItem = (HBITMAP)lpmii->dwTypeData;
4534     }
4535
4536     if (lpmii->fMask & MIIM_FTYPE ) {
4537         if(( lpmii->fType & MFT_BITMAP)) {
4538             SetLastError( ERROR_INVALID_PARAMETER);
4539             return FALSE;
4540         }
4541         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4542         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4543     }
4544     if (lpmii->fMask & MIIM_STRING ) {
4545         /* free the string when used */
4546         HeapFree(GetProcessHeap(), 0, menu->text);
4547         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4548     }
4549
4550     if (lpmii->fMask & MIIM_STATE)
4551     {
4552         /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4553         menu->fState = lpmii->fState;
4554     }
4555
4556     if (lpmii->fMask & MIIM_ID)
4557         menu->wID = lpmii->wID;
4558
4559     if (lpmii->fMask & MIIM_SUBMENU) {
4560         menu->hSubMenu = lpmii->hSubMenu;
4561         if (menu->hSubMenu) {
4562             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4563             if (subMenu) {
4564                 subMenu->wFlags |= MF_POPUP;
4565                 menu->fType |= MF_POPUP;
4566             }
4567             else {
4568                 SetLastError( ERROR_INVALID_PARAMETER);
4569                 return FALSE;
4570             }
4571         }
4572         else
4573             menu->fType &= ~MF_POPUP;
4574     }
4575
4576     if (lpmii->fMask & MIIM_CHECKMARKS)
4577     {
4578         if (lpmii->fType & MFT_RADIOCHECK)
4579             menu->fType |= MFT_RADIOCHECK;
4580
4581         menu->hCheckBit = lpmii->hbmpChecked;
4582         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4583     }
4584     if (lpmii->fMask & MIIM_DATA)
4585         menu->dwItemData = lpmii->dwItemData;
4586
4587     if (lpmii->fMask & MIIM_BITMAP)
4588         menu->hbmpItem = lpmii->hbmpItem;
4589
4590     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4591         menu->fType |= MFT_SEPARATOR;
4592
4593     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4594     return TRUE;
4595 }
4596
4597 /**********************************************************************
4598  *              SetMenuItemInfoA    (USER32.@)
4599  */
4600 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4601                                  const MENUITEMINFOA *lpmii)
4602 {
4603     MENUITEMINFOA mii;
4604     if( lpmii->cbSize != sizeof( mii) &&
4605             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4606         SetLastError( ERROR_INVALID_PARAMETER);
4607         return FALSE;
4608     }
4609     memcpy( &mii, lpmii, lpmii->cbSize);
4610     if( lpmii->cbSize != sizeof( mii)) {
4611         mii.cbSize = sizeof( mii);
4612         mii.hbmpItem = NULL;
4613     }
4614     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4615                                     (const MENUITEMINFOW *)&mii, FALSE);
4616 }
4617
4618 /**********************************************************************
4619  *              SetMenuItemInfoW    (USER32.@)
4620  */
4621 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4622                                  const MENUITEMINFOW *lpmii)
4623 {
4624     MENUITEMINFOW mii;
4625     if( lpmii->cbSize != sizeof( mii) &&
4626             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4627         SetLastError( ERROR_INVALID_PARAMETER);
4628         return FALSE;
4629     }
4630     memcpy( &mii, lpmii, lpmii->cbSize);
4631     if( lpmii->cbSize != sizeof( mii)) {
4632         mii.cbSize = sizeof( mii);
4633         mii.hbmpItem = NULL;
4634     }
4635     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4636                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4637 }
4638
4639 /**********************************************************************
4640  *              SetMenuDefaultItem    (USER32.@)
4641  *
4642  */
4643 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4644 {
4645         UINT i;
4646         POPUPMENU *menu;
4647         MENUITEM *item;
4648
4649         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4650
4651         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4652
4653         /* reset all default-item flags */
4654         item = menu->items;
4655         for (i = 0; i < menu->nItems; i++, item++)
4656         {
4657             item->fState &= ~MFS_DEFAULT;
4658         }
4659
4660         /* no default item */
4661         if ( -1 == uItem)
4662         {
4663             return TRUE;
4664         }
4665
4666         item = menu->items;
4667         if ( bypos )
4668         {
4669             if ( uItem >= menu->nItems ) return FALSE;
4670             item[uItem].fState |= MFS_DEFAULT;
4671             return TRUE;
4672         }
4673         else
4674         {
4675             for (i = 0; i < menu->nItems; i++, item++)
4676             {
4677                 if (item->wID == uItem)
4678                 {
4679                      item->fState |= MFS_DEFAULT;
4680                      return TRUE;
4681                 }
4682             }
4683
4684         }
4685         return FALSE;
4686 }
4687
4688 /**********************************************************************
4689  *              GetMenuDefaultItem    (USER32.@)
4690  */
4691 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4692 {
4693         POPUPMENU *menu;
4694         MENUITEM * item;
4695         UINT i = 0;
4696
4697         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4698
4699         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4700
4701         /* find default item */
4702         item = menu->items;
4703
4704         /* empty menu */
4705         if (! item) return -1;
4706
4707         while ( !( item->fState & MFS_DEFAULT ) )
4708         {
4709             i++; item++;
4710             if  (i >= menu->nItems ) return -1;
4711         }
4712
4713         /* default: don't return disabled items */
4714         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4715
4716         /* search rekursiv when needed */
4717         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4718         {
4719             UINT ret;
4720             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4721             if ( -1 != ret ) return ret;
4722
4723             /* when item not found in submenu, return the popup item */
4724         }
4725         return ( bypos ) ? i : item->wID;
4726
4727 }
4728
4729
4730 /**********************************************************************
4731  *              InsertMenuItemA    (USER32.@)
4732  */
4733 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4734                                 const MENUITEMINFOA *lpmii)
4735 {
4736     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4737     MENUITEMINFOA mii;
4738     if( lpmii->cbSize != sizeof( mii) &&
4739             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4740         SetLastError( ERROR_INVALID_PARAMETER);
4741         return FALSE;
4742     }
4743     memcpy( &mii, lpmii, lpmii->cbSize);
4744     if( lpmii->cbSize != sizeof( mii)) {
4745         mii.cbSize = sizeof( mii);
4746         mii.hbmpItem = NULL;
4747     }
4748     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4749 }
4750
4751
4752 /**********************************************************************
4753  *              InsertMenuItemW    (USER32.@)
4754  */
4755 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4756                                 const MENUITEMINFOW *lpmii)
4757 {
4758     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4759     MENUITEMINFOW mii;
4760     if( lpmii->cbSize != sizeof( mii) &&
4761             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4762         SetLastError( ERROR_INVALID_PARAMETER);
4763         return FALSE;
4764     }
4765     memcpy( &mii, lpmii, lpmii->cbSize);
4766     if( lpmii->cbSize != sizeof( mii)) {
4767         mii.cbSize = sizeof( mii);
4768         mii.hbmpItem = NULL;
4769     }
4770     return SetMenuItemInfo_common(item, &mii, TRUE);
4771 }
4772
4773 /**********************************************************************
4774  *              CheckMenuRadioItem    (USER32.@)
4775  */
4776
4777 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4778                                    UINT first, UINT last, UINT check,
4779                                    UINT bypos)
4780 {
4781      MENUITEM *mifirst, *milast, *micheck;
4782      HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4783
4784      TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4785
4786      mifirst = MENU_FindItem (&mfirst, &first, bypos);
4787      milast = MENU_FindItem (&mlast, &last, bypos);
4788      micheck = MENU_FindItem (&mcheck, &check, bypos);
4789
4790      if (mifirst == NULL || milast == NULL || micheck == NULL ||
4791          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4792          micheck > milast || micheck < mifirst)
4793           return FALSE;
4794
4795      while (mifirst <= milast)
4796      {
4797           if (mifirst == micheck)
4798           {
4799                mifirst->fType |= MFT_RADIOCHECK;
4800                mifirst->fState |= MFS_CHECKED;
4801           } else {
4802                mifirst->fType &= ~MFT_RADIOCHECK;
4803                mifirst->fState &= ~MFS_CHECKED;
4804           }
4805           mifirst++;
4806      }
4807
4808      return TRUE;
4809 }
4810
4811
4812 /**********************************************************************
4813  *              GetMenuItemRect    (USER32.@)
4814  *
4815  *      ATTENTION: Here, the returned values in rect are the screen
4816  *                 coordinates of the item just like if the menu was
4817  *                 always on the upper left side of the application.
4818  *
4819  */
4820 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4821                                  LPRECT rect)
4822 {
4823      POPUPMENU *itemMenu;
4824      MENUITEM *item;
4825      HWND referenceHwnd;
4826
4827      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4828
4829      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4830      referenceHwnd = hwnd;
4831
4832      if(!hwnd)
4833      {
4834          itemMenu = MENU_GetMenu(hMenu);
4835          if (itemMenu == NULL)
4836              return FALSE;
4837
4838          if(itemMenu->hWnd == 0)
4839              return FALSE;
4840          referenceHwnd = itemMenu->hWnd;
4841      }
4842
4843      if ((rect == NULL) || (item == NULL))
4844          return FALSE;
4845
4846      *rect = item->rect;
4847
4848      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4849
4850      return TRUE;
4851 }
4852
4853
4854 /**********************************************************************
4855  *              SetMenuInfo    (USER32.@)
4856  *
4857  * FIXME
4858  *      MIM_APPLYTOSUBMENUS
4859  *      actually use the items to draw the menu
4860  */
4861 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4862 {
4863     POPUPMENU *menu;
4864
4865     TRACE("(%p %p)\n", hMenu, lpmi);
4866
4867     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4868     {
4869
4870         if (lpmi->fMask & MIM_BACKGROUND)
4871             menu->hbrBack = lpmi->hbrBack;
4872
4873         if (lpmi->fMask & MIM_HELPID)
4874             menu->dwContextHelpID = lpmi->dwContextHelpID;
4875
4876         if (lpmi->fMask & MIM_MAXHEIGHT)
4877             menu->cyMax = lpmi->cyMax;
4878
4879         if (lpmi->fMask & MIM_MENUDATA)
4880             menu->dwMenuData = lpmi->dwMenuData;
4881
4882         if (lpmi->fMask & MIM_STYLE)
4883         {
4884             menu->dwStyle = lpmi->dwStyle;
4885             if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4886             if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4887             if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4888             if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4889         }
4890
4891         return TRUE;
4892     }
4893     return FALSE;
4894 }
4895
4896 /**********************************************************************
4897  *              GetMenuInfo    (USER32.@)
4898  *
4899  *  NOTES
4900  *      win98/NT5.0
4901  *
4902  */
4903 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4904 {   POPUPMENU *menu;
4905
4906     TRACE("(%p %p)\n", hMenu, lpmi);
4907
4908     if (lpmi && (menu = MENU_GetMenu(hMenu)))
4909     {
4910
4911         if (lpmi->fMask & MIM_BACKGROUND)
4912             lpmi->hbrBack = menu->hbrBack;
4913
4914         if (lpmi->fMask & MIM_HELPID)
4915             lpmi->dwContextHelpID = menu->dwContextHelpID;
4916
4917         if (lpmi->fMask & MIM_MAXHEIGHT)
4918             lpmi->cyMax = menu->cyMax;
4919
4920         if (lpmi->fMask & MIM_MENUDATA)
4921             lpmi->dwMenuData = menu->dwMenuData;
4922
4923         if (lpmi->fMask & MIM_STYLE)
4924             lpmi->dwStyle = menu->dwStyle;
4925
4926         return TRUE;
4927     }
4928     return FALSE;
4929 }
4930
4931
4932 /**********************************************************************
4933  *         SetMenuContextHelpId    (USER32.@)
4934  */
4935 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4936 {
4937     LPPOPUPMENU menu;
4938
4939     TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4940
4941     if ((menu = MENU_GetMenu(hMenu)))
4942     {
4943         menu->dwContextHelpID = dwContextHelpID;
4944         return TRUE;
4945     }
4946     return FALSE;
4947 }
4948
4949
4950 /**********************************************************************
4951  *         GetMenuContextHelpId    (USER32.@)
4952  */
4953 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4954 {
4955     LPPOPUPMENU menu;
4956
4957     TRACE("(%p)\n", hMenu);
4958
4959     if ((menu = MENU_GetMenu(hMenu)))
4960     {
4961         return menu->dwContextHelpID;
4962     }
4963     return 0;
4964 }
4965
4966 /**********************************************************************
4967  *         MenuItemFromPoint    (USER32.@)
4968  */
4969 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4970 {
4971     POPUPMENU *menu = MENU_GetMenu(hMenu);
4972     UINT pos;
4973
4974     /*FIXME: Do we have to handle hWnd here? */
4975     if (!menu) return -1;
4976     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4977     return pos;
4978 }
4979
4980
4981 /**********************************************************************
4982  *           translate_accelerator
4983  */
4984 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4985                                    BYTE fVirt, WORD key, WORD cmd )
4986 {
4987     INT mask = 0;
4988     UINT mesg = 0;
4989
4990     if (wParam != key) return FALSE;
4991
4992     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4993     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4994     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4995
4996     if (message == WM_CHAR || message == WM_SYSCHAR)
4997     {
4998         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4999         {
5000             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5001             goto found;
5002         }
5003     }
5004     else
5005     {
5006         if(fVirt & FVIRTKEY)
5007         {
5008             TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5009                           wParam, 0xff & HIWORD(lParam));
5010
5011             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5012             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5013         }
5014         else
5015         {
5016             if (!(lParam & 0x01000000))  /* no special_key */
5017             {
5018                 if ((fVirt & FALT) && (lParam & 0x20000000))
5019                 {                              /* ^^ ALT pressed */
5020                     TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5021                     goto found;
5022                 }
5023             }
5024         }
5025     }
5026     return FALSE;
5027
5028  found:
5029     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5030         mesg = 1;
5031     else
5032     {
5033         HMENU hMenu, hSubMenu, hSysMenu;
5034         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5035
5036         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5037         hSysMenu = get_win_sys_menu( hWnd );
5038
5039         /* find menu item and ask application to initialize it */
5040         /* 1. in the system menu */
5041         hSubMenu = hSysMenu;
5042         nPos = cmd;
5043         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5044         {
5045             if (GetCapture())
5046                 mesg = 2;
5047             if (!IsWindowEnabled(hWnd))
5048                 mesg = 3;
5049             else
5050             {
5051                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5052                 if(hSubMenu != hSysMenu)
5053                 {
5054                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5055                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5056                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5057                 }
5058                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5059             }
5060         }
5061         else /* 2. in the window's menu */
5062         {
5063             hSubMenu = hMenu;
5064             nPos = cmd;
5065             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5066             {
5067                 if (GetCapture())
5068                     mesg = 2;
5069                 if (!IsWindowEnabled(hWnd))
5070                     mesg = 3;
5071                 else
5072                 {
5073                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5074                     if(hSubMenu != hMenu)
5075                     {
5076                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5077                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5078                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5079                     }
5080                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5081                 }
5082             }
5083         }
5084
5085         if (mesg == 0)
5086         {
5087             if (uSysStat != (UINT)-1)
5088             {
5089                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5090                     mesg=4;
5091                 else
5092                     mesg=WM_SYSCOMMAND;
5093             }
5094             else
5095             {
5096                 if (uStat != (UINT)-1)
5097                 {
5098                     if (IsIconic(hWnd))
5099                         mesg=5;
5100                     else
5101                     {
5102                         if (uStat & (MF_DISABLED|MF_GRAYED))
5103                             mesg=6;
5104                         else
5105                             mesg=WM_COMMAND;
5106                     }
5107                 }
5108                 else
5109                     mesg=WM_COMMAND;
5110             }
5111         }
5112     }
5113
5114     if( mesg==WM_COMMAND )
5115     {
5116         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5117         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5118     }
5119     else if( mesg==WM_SYSCOMMAND )
5120     {
5121         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5122         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5123     }
5124     else
5125     {
5126         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5127          *   #0: unknown (please report!)
5128          *   #1: for WM_KEYUP,WM_SYSKEYUP
5129          *   #2: mouse is captured
5130          *   #3: window is disabled
5131          *   #4: it's a disabled system menu option
5132          *   #5: it's a menu option, but window is iconic
5133          *   #6: it's a menu option, but disabled
5134          */
5135         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5136         if(mesg==0)
5137             ERR_(accel)(" unknown reason - please report!\n");
5138     }
5139     return TRUE;
5140 }
5141
5142 /**********************************************************************
5143  *      TranslateAcceleratorA     (USER32.@)
5144  *      TranslateAccelerator      (USER32.@)
5145  */
5146 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5147 {
5148     /* YES, Accel16! */
5149     LPACCEL16 lpAccelTbl;
5150     int i;
5151     WPARAM wParam;
5152
5153     if (!hWnd || !msg) return 0;
5154
5155     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5156     {
5157         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5158         return 0;
5159     }
5160
5161     wParam = msg->wParam;
5162
5163     switch (msg->message)
5164     {
5165     case WM_KEYDOWN:
5166     case WM_SYSKEYDOWN:
5167         break;
5168
5169     case WM_CHAR:
5170     case WM_SYSCHAR:
5171         {
5172             char ch = LOWORD(wParam);
5173             WCHAR wch;
5174             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5175             wParam = MAKEWPARAM(wch, HIWORD(wParam));
5176         }
5177         break;
5178
5179     default:
5180         return 0;
5181     }
5182
5183     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5184                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5185     i = 0;
5186     do
5187     {
5188         if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5189                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5190             return 1;
5191     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5192
5193     return 0;
5194 }
5195
5196 /**********************************************************************
5197  *      TranslateAcceleratorW     (USER32.@)
5198  */
5199 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5200 {
5201     /* YES, Accel16! */
5202     LPACCEL16 lpAccelTbl;
5203     int i;
5204
5205     if (!hWnd || !msg) return 0;
5206
5207     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5208     {
5209         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5210         return 0;
5211     }
5212
5213     switch (msg->message)
5214     {
5215     case WM_KEYDOWN:
5216     case WM_SYSKEYDOWN:
5217     case WM_CHAR:
5218     case WM_SYSCHAR:
5219         break;
5220
5221     default:
5222         return 0;
5223     }
5224
5225     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5226                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5227     i = 0;
5228     do
5229     {
5230         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5231                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5232             return 1;
5233     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5234
5235     return 0;
5236 }