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