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