Authors: Jason Edmeades <us@the-edmeades.demon.co.uk>, Christian Costa <titan.costa...
[wine] / controls / 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_CXSIZE );
736             size->cy = GetSystemMetrics( SM_CYSIZE );
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     if (lpitem->fType & MF_OWNERDRAW)
1120     {
1121         /*
1122         ** Experimentation under Windows reveals that an owner-drawn
1123         ** menu is given the rectangle which includes the space it requested
1124         ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1125         ** and a popup-menu arrow.  This is the value of lpitem->rect.
1126         ** Windows will leave all drawing to the application except for
1127         ** the popup-menu arrow.  Windows always draws that itself, after
1128         ** the menu owner has finished drawing.
1129         */
1130         DRAWITEMSTRUCT dis;
1131
1132         dis.CtlType   = ODT_MENU;
1133         dis.CtlID     = 0;
1134         dis.itemID    = lpitem->wID;
1135         dis.itemData  = (DWORD)lpitem->dwItemData;
1136         dis.itemState = 0;
1137         if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1138         if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1139         if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
1140         dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1141         dis.hwndItem   = (HWND)hmenu;
1142         dis.hDC        = hdc;
1143         dis.rcItem     = lpitem->rect;
1144         TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1145               "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1146               dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1147               dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1148               dis.rcItem.bottom);
1149         SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1150         /* Fall through to draw popup-menu arrow */
1151     }
1152
1153     TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1154                                         lpitem->rect.right,lpitem->rect.bottom);
1155
1156     if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1157
1158     rect = lpitem->rect;
1159
1160     if (!(lpitem->fType & MF_OWNERDRAW))
1161     {
1162         if (lpitem->fState & MF_HILITE)
1163         {
1164              if(menuBar)
1165                 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1166              else
1167                 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1168         }
1169         else
1170             FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1171     }
1172
1173     SetBkMode( hdc, TRANSPARENT );
1174
1175     if (!(lpitem->fType & MF_OWNERDRAW))
1176     {
1177         /* vertical separator */
1178         if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1179         {
1180             RECT rc = rect;
1181             rc.top = 3;
1182             rc.bottom = height - 3;
1183             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1184         }
1185
1186         /* horizontal separator */
1187         if (lpitem->fType & MF_SEPARATOR)
1188         {
1189             RECT rc = rect;
1190             rc.left++;
1191             rc.right--;
1192             rc.top += SEPARATOR_HEIGHT / 2;
1193             DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1194             return;
1195         }
1196     }
1197
1198       /* Setup colors */
1199
1200     if (lpitem->fState & MF_HILITE)
1201     {
1202         if(menuBar) {
1203             SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1204             SetBkColor(hdc, GetSysColor(COLOR_MENU));
1205         } else {
1206             if(lpitem->fState & MF_GRAYED)
1207                 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1208             else
1209                 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1210             SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1211         }
1212     }
1213     else
1214     {
1215         if (lpitem->fState & MF_GRAYED)
1216             SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1217         else
1218             SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1219         SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
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)GetWindowLongW(hwndOwner, GWL_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         SetLastError(ERROR_INVALID_MENU_HANDLE);
2566         return FALSE;
2567     }
2568
2569     if (wFlags & TPM_BUTTONDOWN)
2570     {
2571         /* Get the result in order to start the tracking or not */
2572         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2573         fEndMenu = !fRemove;
2574     }
2575
2576     MENU_SetCapture( mt.hOwnerWnd );
2577
2578     while (!fEndMenu)
2579     {
2580         menu = MENU_GetMenu( mt.hCurrentMenu );
2581         if (!menu) /* sometimes happens if I do a window manager close */
2582             break;
2583
2584         /* we have to keep the message in the queue until it's
2585          * clear that menu loop is not over yet. */
2586
2587         for (;;)
2588         {
2589             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2590             {
2591                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2592                 /* remove the message from the queue */
2593                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2594             }
2595             else
2596             {
2597                 if (!enterIdleSent)
2598                 {
2599                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2600                     enterIdleSent = TRUE;
2601                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2602                 }
2603                 WaitMessage();
2604             }
2605         }
2606
2607         /* check if EndMenu() tried to cancel us, by posting this message */
2608         if(msg.message == WM_CANCELMODE)
2609         {
2610             /* we are now out of the loop */
2611             fEndMenu = TRUE;
2612
2613             /* remove the message from the queue */
2614             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2615
2616             /* break out of internal loop, ala ESCAPE */
2617             break;
2618         }
2619
2620         TranslateMessage( &msg );
2621         mt.pt = msg.pt;
2622
2623         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2624           enterIdleSent=FALSE;
2625
2626         fRemove = FALSE;
2627         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2628         {
2629             /*
2630              * Use the mouse coordinates in lParam instead of those in the MSG
2631              * struct to properly handle synthetic messages. They are already
2632              * in screen coordinates.
2633              */
2634             mt.pt.x = (short)LOWORD(msg.lParam);
2635             mt.pt.y = (short)HIWORD(msg.lParam);
2636
2637             /* Find a menu for this mouse event */
2638             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2639
2640             switch(msg.message)
2641             {
2642                 /* no WM_NC... messages in captured state */
2643
2644                 case WM_RBUTTONDBLCLK:
2645                 case WM_RBUTTONDOWN:
2646                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2647                     /* fall through */
2648                 case WM_LBUTTONDBLCLK:
2649                 case WM_LBUTTONDOWN:
2650                     /* If the message belongs to the menu, removes it from the queue */
2651                     /* Else, end menu tracking */
2652                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2653                     fEndMenu = !fRemove;
2654                     break;
2655
2656                 case WM_RBUTTONUP:
2657                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2658                     /* fall through */
2659                 case WM_LBUTTONUP:
2660                     /* Check if a menu was selected by the mouse */
2661                     if (hmenu)
2662                     {
2663                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2664
2665                         /* End the loop if executedMenuId is an item ID */
2666                         /* or if the job was done (executedMenuId = 0). */
2667                         fEndMenu = fRemove = (executedMenuId != -1);
2668                     }
2669                     /* No menu was selected by the mouse */
2670                     /* if the function was called by TrackPopupMenu, continue
2671                        with the menu tracking. If not, stop it */
2672                     else
2673                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2674
2675                     break;
2676
2677                 case WM_MOUSEMOVE:
2678                     /* the selected menu item must be changed every time */
2679                      /* the mouse moves. */
2680
2681                     if (hmenu)
2682                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2683
2684             } /* switch(msg.message) - mouse */
2685         }
2686         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2687         {
2688             fRemove = TRUE;  /* Keyboard messages are always removed */
2689             switch(msg.message)
2690             {
2691             case WM_KEYDOWN:
2692                 switch(msg.wParam)
2693                 {
2694                 case VK_HOME:
2695                 case VK_END:
2696                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2697                                      NO_SELECTED_ITEM, FALSE, 0 );
2698                 /* fall through */
2699                 case VK_UP:
2700                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2701                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2702                     break;
2703
2704                 case VK_DOWN: /* If on menu bar, pull-down the menu */
2705
2706                     menu = MENU_GetMenu( mt.hCurrentMenu );
2707                     if (!(menu->wFlags & MF_POPUP))
2708                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2709                     else      /* otherwise try to move selection */
2710                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2711                     break;
2712
2713                 case VK_LEFT:
2714                     MENU_KeyLeft( &mt, wFlags );
2715                     break;
2716
2717                 case VK_RIGHT:
2718                     MENU_KeyRight( &mt, wFlags );
2719                     break;
2720
2721                 case VK_ESCAPE:
2722                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
2723                     break;
2724
2725                 case VK_F1:
2726                     {
2727                         HELPINFO hi;
2728                         hi.cbSize = sizeof(HELPINFO);
2729                         hi.iContextType = HELPINFO_MENUITEM;
2730                         if (menu->FocusedItem == NO_SELECTED_ITEM)
2731                             hi.iCtrlId = 0;
2732                         else
2733                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2734                         hi.hItemHandle = hmenu;
2735                         hi.dwContextId = menu->dwContextHelpID;
2736                         hi.MousePos = msg.pt;
2737                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2738                         break;
2739                     }
2740
2741                 default:
2742                     break;
2743                 }
2744                 break;  /* WM_KEYDOWN */
2745
2746             case WM_SYSKEYDOWN:
2747                 switch(msg.wParam)
2748                 {
2749                 case VK_MENU:
2750                     fEndMenu = TRUE;
2751                     break;
2752
2753                 }
2754                 break;  /* WM_SYSKEYDOWN */
2755
2756             case WM_CHAR:
2757                 {
2758                     UINT        pos;
2759
2760                     if (msg.wParam == '\r' || msg.wParam == ' ')
2761                     {
2762                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2763                         fEndMenu = (executedMenuId != -1);
2764
2765                         break;
2766                     }
2767
2768                       /* Hack to avoid control chars. */
2769                       /* We will find a better way real soon... */
2770                     if (msg.wParam < 32) break;
2771
2772                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2773                                               LOWORD(msg.wParam), FALSE );
2774                     if (pos == (UINT)-2) fEndMenu = TRUE;
2775                     else if (pos == (UINT)-1) MessageBeep(0);
2776                     else
2777                     {
2778                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2779                                 TRUE, 0 );
2780                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2781                         fEndMenu = (executedMenuId != -1);
2782                     }
2783                 }
2784                 break;
2785             }  /* switch(msg.message) - kbd */
2786         }
2787         else
2788         {
2789             DispatchMessageW( &msg );
2790         }
2791
2792         if (!fEndMenu) fRemove = TRUE;
2793
2794         /* finally remove message from the queue */
2795
2796         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2797             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2798         else mt.trackFlags &= ~TF_SKIPREMOVE;
2799     }
2800
2801     MENU_SetCapture(0);  /* release the capture */
2802
2803     /* If dropdown is still painted and the close box is clicked on
2804        then the menu will be destroyed as part of the DispatchMessage above.
2805        This will then invalidate the menu handle in mt.hTopMenu. We should
2806        check for this first.  */
2807     if( IsMenu( mt.hTopMenu ) )
2808     {
2809         menu = MENU_GetMenu( mt.hTopMenu );
2810
2811         if( IsWindow( mt.hOwnerWnd ) )
2812         {
2813             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2814
2815             if (menu && menu->wFlags & MF_POPUP)
2816             {
2817                 DestroyWindow( menu->hWnd );
2818                 menu->hWnd = 0;
2819             }
2820             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2821             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2822         }
2823
2824         /* Reset the variable for hiding menu */
2825         if( menu ) menu->bTimeToHide = FALSE;
2826     }
2827
2828     /* The return value is only used by TrackPopupMenu */
2829     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2830     if (executedMenuId == -1) executedMenuId = 0;
2831     return executedMenuId;
2832 }
2833
2834 /***********************************************************************
2835  *           MENU_InitTracking
2836  */
2837 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2838 {
2839     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2840
2841     HideCaret(0);
2842
2843     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2844     if (!(wFlags & TPM_NONOTIFY))
2845        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2846
2847     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2848
2849     if (!(wFlags & TPM_NONOTIFY))
2850     {
2851        POPUPMENU *menu;
2852        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2853        if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2854        { /* app changed/recreated menu bar entries in WM_INITMENU
2855             Recalculate menu sizes else clicks will not work */
2856           SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2857                         SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2858
2859        }
2860     }
2861     return TRUE;
2862 }
2863 /***********************************************************************
2864  *           MENU_ExitTracking
2865  */
2866 static BOOL MENU_ExitTracking(HWND hWnd)
2867 {
2868     TRACE("hwnd=%p\n", hWnd);
2869
2870     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2871     ShowCaret(0);
2872     return TRUE;
2873 }
2874
2875 /***********************************************************************
2876  *           MENU_TrackMouseMenuBar
2877  *
2878  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2879  */
2880 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2881 {
2882     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2883     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2884
2885     TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2886
2887     if (IsMenu(hMenu))
2888     {
2889         /* map point to parent client coordinates */
2890         HWND parent = GetAncestor( hWnd, GA_PARENT );
2891         if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2892
2893         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2894         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2895         MENU_ExitTracking(hWnd);
2896     }
2897 }
2898
2899
2900 /***********************************************************************
2901  *           MENU_TrackKbdMenuBar
2902  *
2903  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2904  */
2905 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2906 {
2907     UINT uItem = NO_SELECTED_ITEM;
2908     HMENU hTrackMenu;
2909     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2910
2911     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2912
2913     /* find window that has a menu */
2914
2915     while (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)
2916         if (!(hwnd = GetParent( hwnd ))) return;
2917
2918     /* check if we have to track a system menu */
2919
2920     hTrackMenu = GetMenu( hwnd );
2921     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
2922     {
2923         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
2924         if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED) return;
2925         hTrackMenu = get_win_sys_menu( hwnd );
2926         uItem = 0;
2927         wParam |= HTSYSMENU; /* prevent item lookup */
2928     }
2929
2930     if (!IsMenu( hTrackMenu )) return;
2931
2932     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
2933
2934     if( wChar && wChar != ' ' )
2935     {
2936         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
2937         if( uItem >= (UINT)(-2) )
2938         {
2939             if( uItem == (UINT)(-1) ) MessageBeep(0);
2940             hTrackMenu = 0;
2941         }
2942     }
2943
2944     if( hTrackMenu )
2945     {
2946         MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
2947
2948         if( uItem == NO_SELECTED_ITEM )
2949             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
2950         else if( wChar )
2951             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
2952
2953         MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
2954     }
2955     MENU_ExitTracking( hwnd );
2956 }
2957
2958
2959 /**********************************************************************
2960  *           TrackPopupMenu   (USER32.@)
2961  *
2962  * Like the win32 API, the function return the command ID only if the
2963  * flag TPM_RETURNCMD is on.
2964  *
2965  */
2966 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
2967                            INT nReserved, HWND hWnd, const RECT *lpRect )
2968 {
2969     BOOL ret = FALSE;
2970
2971     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
2972
2973     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2974     if (!(wFlags & TPM_NONOTIFY))
2975         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
2976
2977     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
2978         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
2979     MENU_ExitTracking(hWnd);
2980
2981     return ret;
2982 }
2983
2984 /**********************************************************************
2985  *           TrackPopupMenuEx   (USER32.@)
2986  */
2987 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
2988                                 HWND hWnd, LPTPMPARAMS lpTpm )
2989 {
2990     FIXME("not fully implemented\n" );
2991     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
2992                              lpTpm ? &lpTpm->rcExclude : NULL );
2993 }
2994
2995 /***********************************************************************
2996  *           PopupMenuWndProc
2997  *
2998  * NOTE: Windows has totally different (and undocumented) popup wndproc.
2999  */
3000 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3001 {
3002     TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3003
3004     switch(message)
3005     {
3006     case WM_CREATE:
3007         {
3008             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3009             SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3010             return 0;
3011         }
3012
3013     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3014         return MA_NOACTIVATE;
3015
3016     case WM_PAINT:
3017         {
3018             PAINTSTRUCT ps;
3019             BeginPaint( hwnd, &ps );
3020             MENU_DrawPopupMenu( hwnd, ps.hdc,
3021                                 (HMENU)GetWindowLongW( hwnd, 0 ) );
3022             EndPaint( hwnd, &ps );
3023             return 0;
3024         }
3025     case WM_ERASEBKGND:
3026         return 1;
3027
3028     case WM_DESTROY:
3029         /* zero out global pointer in case resident popup window was destroyed. */
3030         if (hwnd == top_popup) top_popup = 0;
3031         break;
3032
3033     case WM_SHOWWINDOW:
3034
3035         if( wParam )
3036         {
3037             if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3038         }
3039         else
3040             SetWindowLongW( hwnd, 0, 0 );
3041         break;
3042
3043     case MM_SETMENUHANDLE:
3044         SetWindowLongW( hwnd, 0, wParam );
3045         break;
3046
3047     case MM_GETMENUHANDLE:
3048         return GetWindowLongW( hwnd, 0 );
3049
3050     default:
3051         return DefWindowProcW( hwnd, message, wParam, lParam );
3052     }
3053     return 0;
3054 }
3055
3056
3057 /***********************************************************************
3058  *           MENU_GetMenuBarHeight
3059  *
3060  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3061  */
3062 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3063                               INT orgX, INT orgY )
3064 {
3065     HDC hdc;
3066     RECT rectBar;
3067     LPPOPUPMENU lppop;
3068
3069     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3070
3071     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3072
3073     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3074     SelectObject( hdc, hMenuFont);
3075     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3076     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3077     ReleaseDC( hwnd, hdc );
3078     return lppop->Height;
3079 }
3080
3081
3082 /*******************************************************************
3083  *         ChangeMenuA    (USER32.@)
3084  */
3085 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3086                              UINT id, UINT flags )
3087 {
3088     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3089     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3090                                                  id, data );
3091     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3092     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3093                                                 id, data );
3094     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3095                                               flags & MF_BYPOSITION ? pos : id,
3096                                               flags & ~MF_REMOVE );
3097     /* Default: MF_INSERT */
3098     return InsertMenuA( hMenu, pos, flags, id, data );
3099 }
3100
3101
3102 /*******************************************************************
3103  *         ChangeMenuW    (USER32.@)
3104  */
3105 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3106                              UINT id, UINT flags )
3107 {
3108     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3109     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3110                                                  id, data );
3111     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3112     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3113                                                 id, data );
3114     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3115                                               flags & MF_BYPOSITION ? pos : id,
3116                                               flags & ~MF_REMOVE );
3117     /* Default: MF_INSERT */
3118     return InsertMenuW( hMenu, pos, flags, id, data );
3119 }
3120
3121
3122 /*******************************************************************
3123  *         CheckMenuItem    (USER32.@)
3124  */
3125 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3126 {
3127     MENUITEM *item;
3128     DWORD ret;
3129
3130     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3131     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3132     ret = item->fState & MF_CHECKED;
3133     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3134     else item->fState &= ~MF_CHECKED;
3135     return ret;
3136 }
3137
3138
3139 /**********************************************************************
3140  *         EnableMenuItem    (USER32.@)
3141  */
3142 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3143 {
3144     UINT    oldflags;
3145     MENUITEM *item;
3146     POPUPMENU *menu;
3147
3148     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3149
3150     /* Get the Popupmenu to access the owner menu */
3151     if (!(menu = MENU_GetMenu(hMenu)))
3152         return (UINT)-1;
3153
3154     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3155         return (UINT)-1;
3156
3157     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3158     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3159
3160     /* If the close item in the system menu change update the close button */
3161     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3162     {
3163         if (menu->hSysMenuOwner != 0)
3164         {
3165             POPUPMENU* parentMenu;
3166
3167             /* Get the parent menu to access*/
3168             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3169                 return (UINT)-1;
3170
3171             /* Refresh the frame to reflect the change*/
3172             SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3173                          SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3174         }
3175     }
3176
3177     return oldflags;
3178 }
3179
3180
3181 /*******************************************************************
3182  *         GetMenuStringA    (USER32.@)
3183  */
3184 INT WINAPI GetMenuStringA(
3185         HMENU hMenu,    /* [in] menuhandle */
3186         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3187         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3188         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3189         UINT wFlags     /* [in] MF_ flags */
3190 ) {
3191     MENUITEM *item;
3192
3193     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3194     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3195     if (!IS_STRING_ITEM(item->fType)) return 0;
3196     if (!str || !nMaxSiz) return strlenW(item->text);
3197     str[0] = '\0';
3198     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3199         str[nMaxSiz-1] = 0;
3200     TRACE("returning '%s'\n", str );
3201     return strlen(str);
3202 }
3203
3204
3205 /*******************************************************************
3206  *         GetMenuStringW    (USER32.@)
3207  */
3208 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3209                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3210 {
3211     MENUITEM *item;
3212
3213     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3214     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3215     if (!IS_STRING_ITEM(item->fType)) return 0;
3216     if (!str || !nMaxSiz) return strlenW(item->text);
3217     str[0] = '\0';
3218     lstrcpynW( str, item->text, nMaxSiz );
3219     return strlenW(str);
3220 }
3221
3222
3223 /**********************************************************************
3224  *         HiliteMenuItem    (USER32.@)
3225  */
3226 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3227                                 UINT wHilite )
3228 {
3229     LPPOPUPMENU menu;
3230     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3231     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3232     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3233     if (menu->FocusedItem == wItemID) return TRUE;
3234     MENU_HideSubPopups( hWnd, hMenu, FALSE );
3235     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3236     return TRUE;
3237 }
3238
3239
3240 /**********************************************************************
3241  *         GetMenuState    (USER32.@)
3242  */
3243 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3244 {
3245     MENUITEM *item;
3246     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3247     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3248     debug_print_menuitem ("  item: ", item, "");
3249     if (item->fType & MF_POPUP)
3250     {
3251         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3252         if (!menu) return -1;
3253         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3254     }
3255     else
3256     {
3257         /* We used to (from way back then) mask the result to 0xff.  */
3258         /* I don't know why and it seems wrong as the documented */
3259         /* return flag MF_SEPARATOR is outside that mask.  */
3260         return (item->fType | item->fState);
3261     }
3262 }
3263
3264
3265 /**********************************************************************
3266  *         GetMenuItemCount    (USER32.@)
3267  */
3268 INT WINAPI GetMenuItemCount( HMENU hMenu )
3269 {
3270     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3271     if (!menu) return -1;
3272     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3273     return menu->nItems;
3274 }
3275
3276
3277 /**********************************************************************
3278  *         GetMenuItemID    (USER32.@)
3279  */
3280 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3281 {
3282     MENUITEM * lpmi;
3283
3284     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3285     if (lpmi->fType & MF_POPUP) return -1;
3286     return lpmi->wID;
3287
3288 }
3289
3290
3291 /*******************************************************************
3292  *         InsertMenuW    (USER32.@)
3293  */
3294 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3295                          UINT_PTR id, LPCWSTR str )
3296 {
3297     MENUITEM *item;
3298
3299     if (IS_STRING_ITEM(flags) && str)
3300         TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3301               hMenu, pos, flags, id, debugstr_w(str) );
3302     else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3303                hMenu, pos, flags, id, (DWORD)str );
3304
3305     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3306
3307     if (!(MENU_SetItemData( item, flags, id, str )))
3308     {
3309         RemoveMenu( hMenu, pos, flags );
3310         return FALSE;
3311     }
3312
3313     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3314         (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3315
3316     item->hCheckBit = item->hUnCheckBit = 0;
3317     return TRUE;
3318 }
3319
3320
3321 /*******************************************************************
3322  *         InsertMenuA    (USER32.@)
3323  */
3324 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3325                          UINT_PTR id, LPCSTR str )
3326 {
3327     BOOL ret = FALSE;
3328
3329     if (IS_STRING_ITEM(flags) && str)
3330     {
3331         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3332         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3333         if (newstr)
3334         {
3335             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3336             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3337             HeapFree( GetProcessHeap(), 0, newstr );
3338         }
3339         return ret;
3340     }
3341     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3342 }
3343
3344
3345 /*******************************************************************
3346  *         AppendMenuA    (USER32.@)
3347  */
3348 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3349                          UINT_PTR id, LPCSTR data )
3350 {
3351     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3352 }
3353
3354
3355 /*******************************************************************
3356  *         AppendMenuW    (USER32.@)
3357  */
3358 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3359                          UINT_PTR id, LPCWSTR data )
3360 {
3361     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3362 }
3363
3364
3365 /**********************************************************************
3366  *         RemoveMenu    (USER32.@)
3367  */
3368 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3369 {
3370     LPPOPUPMENU menu;
3371     MENUITEM *item;
3372
3373     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3374     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3375     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3376
3377       /* Remove item */
3378
3379     MENU_FreeItemData( item );
3380
3381     if (--menu->nItems == 0)
3382     {
3383         HeapFree( GetProcessHeap(), 0, menu->items );
3384         menu->items = NULL;
3385     }
3386     else
3387     {
3388         while(nPos < menu->nItems)
3389         {
3390             *item = *(item+1);
3391             item++;
3392             nPos++;
3393         }
3394         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3395                                    menu->nItems * sizeof(MENUITEM) );
3396     }
3397     return TRUE;
3398 }
3399
3400
3401 /**********************************************************************
3402  *         DeleteMenu    (USER32.@)
3403  */
3404 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3405 {
3406     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3407     if (!item) return FALSE;
3408     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3409       /* nPos is now the position of the item */
3410     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3411     return TRUE;
3412 }
3413
3414
3415 /*******************************************************************
3416  *         ModifyMenuW    (USER32.@)
3417  */
3418 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3419                          UINT_PTR id, LPCWSTR str )
3420 {
3421     MENUITEM *item;
3422
3423     if (IS_STRING_ITEM(flags))
3424     {
3425         TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3426     }
3427     else
3428     {
3429         TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3430     }
3431
3432     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3433     return MENU_SetItemData( item, flags, id, str );
3434 }
3435
3436
3437 /*******************************************************************
3438  *         ModifyMenuA    (USER32.@)
3439  */
3440 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3441                          UINT_PTR id, LPCSTR str )
3442 {
3443     BOOL ret = FALSE;
3444
3445     if (IS_STRING_ITEM(flags) && str)
3446     {
3447         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3448         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3449         if (newstr)
3450         {
3451             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3452             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3453             HeapFree( GetProcessHeap(), 0, newstr );
3454         }
3455         return ret;
3456     }
3457     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3458 }
3459
3460
3461 /**********************************************************************
3462  *         CreatePopupMenu    (USER32.@)
3463  */
3464 HMENU WINAPI CreatePopupMenu(void)
3465 {
3466     HMENU hmenu;
3467     POPUPMENU *menu;
3468
3469     if (!(hmenu = CreateMenu())) return 0;
3470     menu = MENU_GetMenu( hmenu );
3471     menu->wFlags |= MF_POPUP;
3472     menu->bTimeToHide = FALSE;
3473     return hmenu;
3474 }
3475
3476
3477 /**********************************************************************
3478  *         GetMenuCheckMarkDimensions    (USER.417)
3479  *         GetMenuCheckMarkDimensions    (USER32.@)
3480  */
3481 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3482 {
3483     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3484 }
3485
3486
3487 /**********************************************************************
3488  *         SetMenuItemBitmaps    (USER32.@)
3489  */
3490 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3491                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3492 {
3493     MENUITEM *item;
3494     TRACE("(%p, %04x, %04x, %p, %p)\n",
3495           hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3496     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3497
3498     if (!hNewCheck && !hNewUnCheck)
3499     {
3500         item->fState &= ~MF_USECHECKBITMAPS;
3501     }
3502     else  /* Install new bitmaps */
3503     {
3504         item->hCheckBit = hNewCheck;
3505         item->hUnCheckBit = hNewUnCheck;
3506         item->fState |= MF_USECHECKBITMAPS;
3507     }
3508     return TRUE;
3509 }
3510
3511
3512 /**********************************************************************
3513  *         CreateMenu    (USER32.@)
3514  */
3515 HMENU WINAPI CreateMenu(void)
3516 {
3517     HMENU hMenu;
3518     LPPOPUPMENU menu;
3519     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3520     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3521
3522     ZeroMemory(menu, sizeof(POPUPMENU));
3523     menu->wMagic = MENU_MAGIC;
3524     menu->FocusedItem = NO_SELECTED_ITEM;
3525     menu->bTimeToHide = FALSE;
3526
3527     TRACE("return %p\n", hMenu );
3528
3529     return hMenu;
3530 }
3531
3532
3533 /**********************************************************************
3534  *         DestroyMenu    (USER32.@)
3535  */
3536 BOOL WINAPI DestroyMenu( HMENU hMenu )
3537 {
3538     TRACE("(%p)\n", hMenu);
3539
3540     /* Silently ignore attempts to destroy default system popup */
3541
3542     if (hMenu && hMenu != MENU_DefSysPopup)
3543     {
3544         LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3545
3546         if (!lppop) return FALSE;
3547
3548         lppop->wMagic = 0;  /* Mark it as destroyed */
3549
3550         if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3551         {
3552             DestroyWindow( lppop->hWnd );
3553             lppop->hWnd = 0;
3554         }
3555
3556         if (lppop->items) /* recursively destroy submenus */
3557         {
3558             int i;
3559             MENUITEM *item = lppop->items;
3560             for (i = lppop->nItems; i > 0; i--, item++)
3561             {
3562                 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3563                 MENU_FreeItemData( item );
3564             }
3565             HeapFree( GetProcessHeap(), 0, lppop->items );
3566         }
3567         USER_HEAP_FREE( hMenu );
3568     }
3569     return (hMenu != MENU_DefSysPopup);
3570 }
3571
3572
3573 /**********************************************************************
3574  *         GetSystemMenu    (USER32.@)
3575  */
3576 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3577 {
3578     WND *wndPtr = WIN_FindWndPtr( hWnd );
3579     HMENU retvalue = 0;
3580
3581     if (wndPtr)
3582     {
3583         if( wndPtr->hSysMenu )
3584         {
3585             if( bRevert )
3586             {
3587                 DestroyMenu(wndPtr->hSysMenu);
3588                 wndPtr->hSysMenu = 0;
3589             }
3590             else
3591             {
3592                 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3593                 if( menu )
3594                 {
3595                    if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3596                       menu->items[0].hSubMenu = MENU_CopySysPopup();
3597                 }
3598                 else
3599                 {
3600                    WARN("Current sys-menu (%p) of wnd %p is broken\n",
3601                         wndPtr->hSysMenu, hWnd);
3602                    wndPtr->hSysMenu = 0;
3603                 }
3604             }
3605         }
3606
3607         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3608             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3609
3610         if( wndPtr->hSysMenu )
3611         {
3612             POPUPMENU *menu;
3613             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3614
3615             /* Store the dummy sysmenu handle to facilitate the refresh */
3616             /* of the close button if the SC_CLOSE item change */
3617             menu = MENU_GetMenu(retvalue);
3618             if ( menu )
3619                menu->hSysMenuOwner = wndPtr->hSysMenu;
3620         }
3621         WIN_ReleaseWndPtr(wndPtr);
3622     }
3623     return bRevert ? 0 : retvalue;
3624 }
3625
3626
3627 /*******************************************************************
3628  *         SetSystemMenu    (USER32.@)
3629  */
3630 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3631 {
3632     WND *wndPtr = WIN_FindWndPtr(hwnd);
3633
3634     if (wndPtr)
3635     {
3636         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3637         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3638         WIN_ReleaseWndPtr(wndPtr);
3639         return TRUE;
3640     }
3641     return FALSE;
3642 }
3643
3644
3645 /**********************************************************************
3646  *         GetMenu    (USER32.@)
3647  */
3648 HMENU WINAPI GetMenu( HWND hWnd )
3649 {
3650     HMENU retvalue = (HMENU)GetWindowLongW( hWnd, GWL_ID );
3651     TRACE("for %p returning %p\n", hWnd, retvalue);
3652     return retvalue;
3653 }
3654
3655
3656 /**********************************************************************
3657  *         MENU_SetMenu
3658  *
3659  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3660  * SetWindowPos call that would result if SetMenu were called directly.
3661  */
3662 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3663 {
3664     TRACE("(%p, %p);\n", hWnd, hMenu);
3665
3666     if (hMenu && !IsMenu(hMenu))
3667     {
3668         WARN("hMenu %p is not a menu handle\n", hMenu);
3669         return FALSE;
3670     }
3671     if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3672
3673     hWnd = WIN_GetFullHandle( hWnd );
3674     if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
3675
3676     if (hMenu != 0)
3677     {
3678         LPPOPUPMENU lpmenu;
3679
3680         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3681
3682         lpmenu->hWnd = hWnd;
3683         lpmenu->Height = 0;  /* Make sure we recalculate the size */
3684     }
3685     SetWindowLongW( hWnd, GWL_ID, (LONG_PTR)hMenu );
3686     return TRUE;
3687 }
3688
3689
3690 /**********************************************************************
3691  *         SetMenu    (USER32.@)
3692  */
3693 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3694 {   
3695     if(!MENU_SetMenu(hWnd, hMenu))
3696         return FALSE;
3697  
3698     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3699                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3700     return TRUE;
3701 }
3702
3703
3704 /**********************************************************************
3705  *         GetSubMenu    (USER32.@)
3706  */
3707 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3708 {
3709     MENUITEM * lpmi;
3710
3711     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3712     if (!(lpmi->fType & MF_POPUP)) return 0;
3713     return lpmi->hSubMenu;
3714 }
3715
3716
3717 /**********************************************************************
3718  *         DrawMenuBar    (USER32.@)
3719  */
3720 BOOL WINAPI DrawMenuBar( HWND hWnd )
3721 {
3722     LPPOPUPMENU lppop;
3723     HMENU hMenu = GetMenu(hWnd);
3724
3725     if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3726     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3727
3728     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3729     lppop->hwndOwner = hWnd;
3730     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3731                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3732     return TRUE;
3733 }
3734
3735 /***********************************************************************
3736  *           DrawMenuBarTemp   (USER32.@)
3737  *
3738  * UNDOCUMENTED !!
3739  *
3740  * called by W98SE desk.cpl Control Panel Applet
3741  *
3742  * Not 100% sure about the param names, but close.
3743  */
3744 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3745 {
3746     LPPOPUPMENU lppop;
3747     UINT i,retvalue;
3748     HFONT hfontOld = 0;
3749
3750     if (!hMenu)
3751         hMenu = GetMenu(hwnd);
3752
3753     if (!hFont)
3754         hFont = hMenuFont;
3755
3756     lppop = MENU_GetMenu( hMenu );
3757     if (lppop == NULL || lprect == NULL)
3758     {
3759         retvalue = GetSystemMetrics(SM_CYMENU);
3760         goto END;
3761     }
3762
3763     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3764
3765     hfontOld = SelectObject( hDC, hFont);
3766
3767     if (lppop->Height == 0)
3768         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3769
3770     lprect->bottom = lprect->top + lppop->Height;
3771
3772     FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3773
3774     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3775     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3776     LineTo( hDC, lprect->right, lprect->bottom );
3777
3778     if (lppop->nItems == 0)
3779     {
3780         retvalue = GetSystemMetrics(SM_CYMENU);
3781         goto END;
3782     }
3783
3784     for (i = 0; i < lppop->nItems; i++)
3785     {
3786         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3787                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3788     }
3789     retvalue = lppop->Height;
3790
3791 END:
3792     if (hfontOld) SelectObject (hDC, hfontOld);
3793     return retvalue;
3794 }
3795
3796 /***********************************************************************
3797  *           EndMenu   (USER.187)
3798  *           EndMenu   (USER32.@)
3799  */
3800 void WINAPI EndMenu(void)
3801 {
3802     /* if we are in the menu code, and it is active */
3803     if (!fEndMenu && top_popup)
3804     {
3805         /* terminate the menu handling code */
3806         fEndMenu = TRUE;
3807
3808         /* needs to be posted to wakeup the internal menu handler */
3809         /* which will now terminate the menu, in the event that */
3810         /* the main window was minimized, or lost focus, so we */
3811         /* don't end up with an orphaned menu */
3812         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3813     }
3814 }
3815
3816
3817 /***********************************************************************
3818  *           LookupMenuHandle   (USER.217)
3819  */
3820 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3821 {
3822     HMENU hmenu32 = HMENU_32(hmenu);
3823     UINT id32 = id;
3824     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3825     else return HMENU_16(hmenu32);
3826 }
3827
3828
3829 /**********************************************************************
3830  *          LoadMenu    (USER.150)
3831  */
3832 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3833 {
3834     HRSRC16 hRsrc;
3835     HGLOBAL16 handle;
3836     HMENU16 hMenu;
3837
3838     if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3839     if (!name) return 0;
3840
3841     instance = GetExePtr( instance );
3842     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3843     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3844     hMenu = LoadMenuIndirect16(LockResource16(handle));
3845     FreeResource16( handle );
3846     return hMenu;
3847 }
3848
3849
3850 /*****************************************************************
3851  *        LoadMenuA   (USER32.@)
3852  */
3853 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3854 {
3855     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3856     if (!hrsrc) return 0;
3857     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3858 }
3859
3860
3861 /*****************************************************************
3862  *        LoadMenuW   (USER32.@)
3863  */
3864 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3865 {
3866     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3867     if (!hrsrc) return 0;
3868     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3869 }
3870
3871
3872 /**********************************************************************
3873  *          LoadMenuIndirect    (USER.220)
3874  */
3875 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3876 {
3877     HMENU hMenu;
3878     WORD version, offset;
3879     LPCSTR p = (LPCSTR)template;
3880
3881     TRACE("(%p)\n", template );
3882     version = GET_WORD(p);
3883     p += sizeof(WORD);
3884     if (version)
3885     {
3886         WARN("version must be 0 for Win16\n" );
3887         return 0;
3888     }
3889     offset = GET_WORD(p);
3890     p += sizeof(WORD) + offset;
3891     if (!(hMenu = CreateMenu())) return 0;
3892     if (!MENU_ParseResource( p, hMenu, FALSE ))
3893     {
3894         DestroyMenu( hMenu );
3895         return 0;
3896     }
3897     return HMENU_16(hMenu);
3898 }
3899
3900
3901 /**********************************************************************
3902  *          LoadMenuIndirectW    (USER32.@)
3903  */
3904 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
3905 {
3906     HMENU hMenu;
3907     WORD version, offset;
3908     LPCSTR p = (LPCSTR)template;
3909
3910     version = GET_WORD(p);
3911     p += sizeof(WORD);
3912     TRACE("%p, ver %d\n", template, version );
3913     switch (version)
3914     {
3915       case 0: /* standard format is version of 0 */
3916         offset = GET_WORD(p);
3917         p += sizeof(WORD) + offset;
3918         if (!(hMenu = CreateMenu())) return 0;
3919         if (!MENU_ParseResource( p, hMenu, TRUE ))
3920           {
3921             DestroyMenu( hMenu );
3922             return 0;
3923           }
3924         return hMenu;
3925       case 1: /* extended format is version of 1 */
3926         offset = GET_WORD(p);
3927         p += sizeof(WORD) + offset;
3928         if (!(hMenu = CreateMenu())) return 0;
3929         if (!MENUEX_ParseResource( p, hMenu))
3930           {
3931             DestroyMenu( hMenu );
3932             return 0;
3933           }
3934         return hMenu;
3935       default:
3936         ERR("version %d not supported.\n", version);
3937         return 0;
3938     }
3939 }
3940
3941
3942 /**********************************************************************
3943  *          LoadMenuIndirectA    (USER32.@)
3944  */
3945 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
3946 {
3947     return LoadMenuIndirectW( template );
3948 }
3949
3950
3951 /**********************************************************************
3952  *              IsMenu    (USER32.@)
3953  */
3954 BOOL WINAPI IsMenu(HMENU hmenu)
3955 {
3956     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
3957     return menu != NULL;
3958 }
3959
3960 /**********************************************************************
3961  *              GetMenuItemInfo_common
3962  */
3963
3964 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
3965                                         LPMENUITEMINFOW lpmii, BOOL unicode)
3966 {
3967     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
3968
3969     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
3970
3971     if (!menu)
3972         return FALSE;
3973
3974     if (lpmii->fMask & MIIM_TYPE) {
3975         lpmii->fType = menu->fType;
3976         switch (MENU_ITEM_TYPE(menu->fType)) {
3977         case MF_STRING:
3978             break;  /* will be done below */
3979         case MF_OWNERDRAW:
3980         case MF_BITMAP:
3981             lpmii->dwTypeData = menu->text;
3982             /* fall through */
3983         default:
3984             lpmii->cch = 0;
3985         }
3986     }
3987
3988     /* copy the text string */
3989     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
3990          (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
3991     {
3992         int len;
3993         if (unicode)
3994         {
3995             len = strlenW(menu->text);
3996             if(lpmii->dwTypeData && lpmii->cch)
3997                 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
3998         }
3999         else
4000         {
4001             len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4002             if(lpmii->dwTypeData && lpmii->cch)
4003                 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4004                                           (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4005                     ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4006         }
4007         /* if we've copied a substring we return its length */
4008         if(lpmii->dwTypeData && lpmii->cch)
4009         {
4010             if (lpmii->cch <= len) lpmii->cch--;
4011         }
4012         else /* return length of string */
4013             lpmii->cch = len;
4014     }
4015
4016     if (lpmii->fMask & MIIM_FTYPE)
4017         lpmii->fType = menu->fType;
4018
4019     if (lpmii->fMask & MIIM_BITMAP)
4020         lpmii->hbmpItem = menu->hbmpItem;
4021
4022     if (lpmii->fMask & MIIM_STATE)
4023         lpmii->fState = menu->fState;
4024
4025     if (lpmii->fMask & MIIM_ID)
4026         lpmii->wID = menu->wID;
4027
4028     if (lpmii->fMask & MIIM_SUBMENU)
4029         lpmii->hSubMenu = menu->hSubMenu;
4030
4031     if (lpmii->fMask & MIIM_CHECKMARKS) {
4032         lpmii->hbmpChecked = menu->hCheckBit;
4033         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4034     }
4035     if (lpmii->fMask & MIIM_DATA)
4036         lpmii->dwItemData = menu->dwItemData;
4037
4038   return TRUE;
4039 }
4040
4041 /**********************************************************************
4042  *              GetMenuItemInfoA    (USER32.@)
4043  */
4044 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4045                                   LPMENUITEMINFOA lpmii)
4046 {
4047     return GetMenuItemInfo_common (hmenu, item, bypos,
4048                                     (LPMENUITEMINFOW)lpmii, FALSE);
4049 }
4050
4051 /**********************************************************************
4052  *              GetMenuItemInfoW    (USER32.@)
4053  */
4054 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4055                                   LPMENUITEMINFOW lpmii)
4056 {
4057     return GetMenuItemInfo_common (hmenu, item, bypos,
4058                                      lpmii, TRUE);
4059 }
4060
4061
4062 /* set a menu item text from a ASCII or Unicode string */
4063 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4064 {
4065     if (!text)
4066     {
4067         menu->text = NULL;
4068         menu->fType |= MF_SEPARATOR;
4069     }
4070     else if (unicode)
4071     {
4072         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4073             strcpyW( menu->text, text );
4074     }
4075     else
4076     {
4077         LPCSTR str = (LPCSTR)text;
4078         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4079         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4080             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4081     }
4082 }
4083
4084
4085 /**********************************************************************
4086  *              SetMenuItemInfo_common
4087  */
4088
4089 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4090                                        const MENUITEMINFOW *lpmii,
4091                                        BOOL unicode)
4092 {
4093     if (!menu) return FALSE;
4094
4095     debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4096
4097     if (lpmii->fMask & MIIM_TYPE ) {
4098         /* Get rid of old string. */
4099         if (IS_STRING_ITEM(menu->fType) && menu->text) {
4100             HeapFree(GetProcessHeap(), 0, menu->text);
4101             menu->text = NULL;
4102         }
4103
4104         /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4105         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4106         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4107
4108         menu->text = lpmii->dwTypeData;
4109
4110        if (IS_STRING_ITEM(menu->fType))
4111            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4112     }
4113
4114     if (lpmii->fMask & MIIM_FTYPE ) {
4115         /* free the string when the type is changing */
4116         if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4117             HeapFree(GetProcessHeap(), 0, menu->text);
4118             menu->text = NULL;
4119         }
4120         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4121         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4122         if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4123             menu->fType |= MF_SEPARATOR;
4124     }
4125
4126     if (lpmii->fMask & MIIM_STRING ) {
4127         if (IS_STRING_ITEM(menu->fType)) {
4128             /* free the string when used */
4129             if(menu->text)
4130                 HeapFree(GetProcessHeap(), 0, menu->text);
4131             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4132         }
4133     }
4134
4135     if (lpmii->fMask & MIIM_STATE)
4136     {
4137         /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4138         menu->fState = lpmii->fState;
4139     }
4140
4141     if (lpmii->fMask & MIIM_ID)
4142         menu->wID = lpmii->wID;
4143
4144     if (lpmii->fMask & MIIM_SUBMENU) {
4145         menu->hSubMenu = lpmii->hSubMenu;
4146         if (menu->hSubMenu) {
4147             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4148             if (subMenu) {
4149                 subMenu->wFlags |= MF_POPUP;
4150                 menu->fType |= MF_POPUP;
4151             }
4152             else
4153                 /* FIXME: Return an error ? */
4154                 menu->fType &= ~MF_POPUP;
4155         }
4156         else
4157             menu->fType &= ~MF_POPUP;
4158     }
4159
4160     if (lpmii->fMask & MIIM_CHECKMARKS)
4161     {
4162         if (lpmii->fType & MFT_RADIOCHECK)
4163             menu->fType |= MFT_RADIOCHECK;
4164
4165         menu->hCheckBit = lpmii->hbmpChecked;
4166         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4167     }
4168     if (lpmii->fMask & MIIM_DATA)
4169         menu->dwItemData = lpmii->dwItemData;
4170
4171     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4172     return TRUE;
4173 }
4174
4175 /**********************************************************************
4176  *              SetMenuItemInfoA    (USER32.@)
4177  */
4178 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4179                                  const MENUITEMINFOA *lpmii)
4180 {
4181     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4182                                     (const MENUITEMINFOW *)lpmii, FALSE);
4183 }
4184
4185 /**********************************************************************
4186  *              SetMenuItemInfoW    (USER32.@)
4187  */
4188 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4189                                  const MENUITEMINFOW *lpmii)
4190 {
4191     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4192                                     lpmii, TRUE);
4193 }
4194
4195 /**********************************************************************
4196  *              SetMenuDefaultItem    (USER32.@)
4197  *
4198  */
4199 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4200 {
4201         UINT i;
4202         POPUPMENU *menu;
4203         MENUITEM *item;
4204
4205         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4206
4207         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4208
4209         /* reset all default-item flags */
4210         item = menu->items;
4211         for (i = 0; i < menu->nItems; i++, item++)
4212         {
4213             item->fState &= ~MFS_DEFAULT;
4214         }
4215
4216         /* no default item */
4217         if ( -1 == uItem)
4218         {
4219             return TRUE;
4220         }
4221
4222         item = menu->items;
4223         if ( bypos )
4224         {
4225             if ( uItem >= menu->nItems ) return FALSE;
4226             item[uItem].fState |= MFS_DEFAULT;
4227             return TRUE;
4228         }
4229         else
4230         {
4231             for (i = 0; i < menu->nItems; i++, item++)
4232             {
4233                 if (item->wID == uItem)
4234                 {
4235                      item->fState |= MFS_DEFAULT;
4236                      return TRUE;
4237                 }
4238             }
4239
4240         }
4241         return FALSE;
4242 }
4243
4244 /**********************************************************************
4245  *              GetMenuDefaultItem    (USER32.@)
4246  */
4247 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4248 {
4249         POPUPMENU *menu;
4250         MENUITEM * item;
4251         UINT i = 0;
4252
4253         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4254
4255         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4256
4257         /* find default item */
4258         item = menu->items;
4259
4260         /* empty menu */
4261         if (! item) return -1;
4262
4263         while ( !( item->fState & MFS_DEFAULT ) )
4264         {
4265             i++; item++;
4266             if  (i >= menu->nItems ) return -1;
4267         }
4268
4269         /* default: don't return disabled items */
4270         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4271
4272         /* search rekursiv when needed */
4273         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4274         {
4275             UINT ret;
4276             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4277             if ( -1 != ret ) return ret;
4278
4279             /* when item not found in submenu, return the popup item */
4280         }
4281         return ( bypos ) ? i : item->wID;
4282
4283 }
4284
4285
4286 /**********************************************************************
4287  *              InsertMenuItemA    (USER32.@)
4288  */
4289 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4290                                 const MENUITEMINFOA *lpmii)
4291 {
4292     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4293     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4294 }
4295
4296
4297 /**********************************************************************
4298  *              InsertMenuItemW    (USER32.@)
4299  */
4300 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4301                                 const MENUITEMINFOW *lpmii)
4302 {
4303     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4304     return SetMenuItemInfo_common(item, lpmii, TRUE);
4305 }
4306
4307 /**********************************************************************
4308  *              CheckMenuRadioItem    (USER32.@)
4309  */
4310
4311 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4312                                    UINT first, UINT last, UINT check,
4313                                    UINT bypos)
4314 {
4315      MENUITEM *mifirst, *milast, *micheck;
4316      HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4317
4318      TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4319
4320      mifirst = MENU_FindItem (&mfirst, &first, bypos);
4321      milast = MENU_FindItem (&mlast, &last, bypos);
4322      micheck = MENU_FindItem (&mcheck, &check, bypos);
4323
4324      if (mifirst == NULL || milast == NULL || micheck == NULL ||
4325          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4326          micheck > milast || micheck < mifirst)
4327           return FALSE;
4328
4329      while (mifirst <= milast)
4330      {
4331           if (mifirst == micheck)
4332           {
4333                mifirst->fType |= MFT_RADIOCHECK;
4334                mifirst->fState |= MFS_CHECKED;
4335           } else {
4336                mifirst->fType &= ~MFT_RADIOCHECK;
4337                mifirst->fState &= ~MFS_CHECKED;
4338           }
4339           mifirst++;
4340      }
4341
4342      return TRUE;
4343 }
4344
4345
4346 /**********************************************************************
4347  *              GetMenuItemRect    (USER32.@)
4348  *
4349  *      ATTENTION: Here, the returned values in rect are the screen
4350  *                 coordinates of the item just like if the menu was
4351  *                 always on the upper left side of the application.
4352  *
4353  */
4354 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4355                                  LPRECT rect)
4356 {
4357      POPUPMENU *itemMenu;
4358      MENUITEM *item;
4359      HWND referenceHwnd;
4360
4361      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4362
4363      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4364      referenceHwnd = hwnd;
4365
4366      if(!hwnd)
4367      {
4368          itemMenu = MENU_GetMenu(hMenu);
4369          if (itemMenu == NULL)
4370              return FALSE;
4371
4372          if(itemMenu->hWnd == 0)
4373              return FALSE;
4374          referenceHwnd = itemMenu->hWnd;
4375      }
4376
4377      if ((rect == NULL) || (item == NULL))
4378          return FALSE;
4379
4380      *rect = item->rect;
4381
4382      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4383
4384      return TRUE;
4385 }
4386
4387
4388 /**********************************************************************
4389  *              SetMenuInfo    (USER32.@)
4390  *
4391  * FIXME
4392  *      MIM_APPLYTOSUBMENUS
4393  *      actually use the items to draw the menu
4394  */
4395 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4396 {
4397     POPUPMENU *menu;
4398
4399     TRACE("(%p %p)\n", hMenu, lpmi);
4400
4401     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4402     {
4403
4404         if (lpmi->fMask & MIM_BACKGROUND)
4405             menu->hbrBack = lpmi->hbrBack;
4406
4407         if (lpmi->fMask & MIM_HELPID)
4408             menu->dwContextHelpID = lpmi->dwContextHelpID;
4409
4410         if (lpmi->fMask & MIM_MAXHEIGHT)
4411             menu->cyMax = lpmi->cyMax;
4412
4413         if (lpmi->fMask & MIM_MENUDATA)
4414             menu->dwMenuData = lpmi->dwMenuData;
4415
4416         if (lpmi->fMask & MIM_STYLE)
4417             menu->dwStyle = lpmi->dwStyle;
4418
4419         return TRUE;
4420     }
4421     return FALSE;
4422 }
4423
4424 /**********************************************************************
4425  *              GetMenuInfo    (USER32.@)
4426  *
4427  *  NOTES
4428  *      win98/NT5.0
4429  *
4430  */
4431 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4432 {   POPUPMENU *menu;
4433
4434     TRACE("(%p %p)\n", hMenu, lpmi);
4435
4436     if (lpmi && (menu = MENU_GetMenu(hMenu)))
4437     {
4438
4439         if (lpmi->fMask & MIM_BACKGROUND)
4440             lpmi->hbrBack = menu->hbrBack;
4441
4442         if (lpmi->fMask & MIM_HELPID)
4443             lpmi->dwContextHelpID = menu->dwContextHelpID;
4444
4445         if (lpmi->fMask & MIM_MAXHEIGHT)
4446             lpmi->cyMax = menu->cyMax;
4447
4448         if (lpmi->fMask & MIM_MENUDATA)
4449             lpmi->dwMenuData = menu->dwMenuData;
4450
4451         if (lpmi->fMask & MIM_STYLE)
4452             lpmi->dwStyle = menu->dwStyle;
4453
4454         return TRUE;
4455     }
4456     return FALSE;
4457 }
4458
4459
4460 /**********************************************************************
4461  *         SetMenuContextHelpId    (USER32.@)
4462  */
4463 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4464 {
4465     LPPOPUPMENU menu;
4466
4467     TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4468
4469     if ((menu = MENU_GetMenu(hMenu)))
4470     {
4471         menu->dwContextHelpID = dwContextHelpID;
4472         return TRUE;
4473     }
4474     return FALSE;
4475 }
4476
4477
4478 /**********************************************************************
4479  *         GetMenuContextHelpId    (USER32.@)
4480  */
4481 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4482 {
4483     LPPOPUPMENU menu;
4484
4485     TRACE("(%p)\n", hMenu);
4486
4487     if ((menu = MENU_GetMenu(hMenu)))
4488     {
4489         return menu->dwContextHelpID;
4490     }
4491     return 0;
4492 }
4493
4494 /**********************************************************************
4495  *         MenuItemFromPoint    (USER32.@)
4496  */
4497 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4498 {
4499     POPUPMENU *menu = MENU_GetMenu(hMenu);
4500     UINT pos;
4501     MENUITEM *item;
4502
4503     /*FIXME: Do we have to handle hWnd here? */
4504     item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4505
4506     return pos;
4507 }
4508
4509
4510 /**********************************************************************
4511  *           translate_accelerator
4512  */
4513 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4514                                    BYTE fVirt, WORD key, WORD cmd )
4515 {
4516     UINT mesg = 0;
4517
4518     if (wParam != key) return FALSE;
4519
4520     if (message == WM_CHAR)
4521     {
4522         if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4523         {
4524             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4525             goto found;
4526         }
4527     }
4528     else
4529     {
4530         if(fVirt & FVIRTKEY)
4531         {
4532             INT mask = 0;
4533             TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4534                           wParam, 0xff & HIWORD(lParam));
4535             if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4536             if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4537             if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4538             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4539             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4540         }
4541         else
4542         {
4543             if (!(lParam & 0x01000000))  /* no special_key */
4544             {
4545                 if ((fVirt & FALT) && (lParam & 0x20000000))
4546                 {                              /* ^^ ALT pressed */
4547                     TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4548                     goto found;
4549                 }
4550             }
4551         }
4552     }
4553     return FALSE;
4554
4555  found:
4556     if (message == WM_KEYUP || message == WM_SYSKEYUP)
4557         mesg = 1;
4558     else
4559     {
4560         HMENU hMenu, hSubMenu, hSysMenu;
4561         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4562
4563         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4564         hSysMenu = get_win_sys_menu( hWnd );
4565
4566         /* find menu item and ask application to initialize it */
4567         /* 1. in the system menu */
4568         hSubMenu = hSysMenu;
4569         nPos = cmd;
4570         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4571         {
4572             if (GetCapture())
4573                 mesg = 2;
4574             if (!IsWindowEnabled(hWnd))
4575                 mesg = 3;
4576             else
4577             {
4578                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4579                 if(hSubMenu != hSysMenu)
4580                 {
4581                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4582                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4583                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4584                 }
4585                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4586             }
4587         }
4588         else /* 2. in the window's menu */
4589         {
4590             hSubMenu = hMenu;
4591             nPos = cmd;
4592             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4593             {
4594                 if (GetCapture())
4595                     mesg = 2;
4596                 if (!IsWindowEnabled(hWnd))
4597                     mesg = 3;
4598                 else
4599                 {
4600                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4601                     if(hSubMenu != hMenu)
4602                     {
4603                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4604                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4605                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4606                     }
4607                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4608                 }
4609             }
4610         }
4611
4612         if (mesg == 0)
4613         {
4614             if (uSysStat != (UINT)-1)
4615             {
4616                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4617                     mesg=4;
4618                 else
4619                     mesg=WM_SYSCOMMAND;
4620             }
4621             else
4622             {
4623                 if (uStat != (UINT)-1)
4624                 {
4625                     if (IsIconic(hWnd))
4626                         mesg=5;
4627                     else
4628                     {
4629                         if (uStat & (MF_DISABLED|MF_GRAYED))
4630                             mesg=6;
4631                         else
4632                             mesg=WM_COMMAND;
4633                     }
4634                 }
4635                 else
4636                     mesg=WM_COMMAND;
4637             }
4638         }
4639     }
4640
4641     if( mesg==WM_COMMAND )
4642     {
4643         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4644         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4645     }
4646     else if( mesg==WM_SYSCOMMAND )
4647     {
4648         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4649         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4650     }
4651     else
4652     {
4653         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
4654          *   #0: unknown (please report!)
4655          *   #1: for WM_KEYUP,WM_SYSKEYUP
4656          *   #2: mouse is captured
4657          *   #3: window is disabled
4658          *   #4: it's a disabled system menu option
4659          *   #5: it's a menu option, but window is iconic
4660          *   #6: it's a menu option, but disabled
4661          */
4662         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4663         if(mesg==0)
4664             ERR_(accel)(" unknown reason - please report!\n");
4665     }
4666     return TRUE;
4667 }
4668
4669 /**********************************************************************
4670  *      TranslateAccelerator      (USER32.@)
4671  *      TranslateAcceleratorA     (USER32.@)
4672  *      TranslateAcceleratorW     (USER32.@)
4673  */
4674 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4675 {
4676     /* YES, Accel16! */
4677     LPACCEL16 lpAccelTbl;
4678     int i;
4679
4680     if (msg == NULL)
4681     {
4682         WARN_(accel)("msg null; should hang here to be win compatible\n");
4683         return 0;
4684     }
4685     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4686     {
4687         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4688         return 0;
4689     }
4690     if ((msg->message != WM_KEYDOWN &&
4691          msg->message != WM_KEYUP &&
4692          msg->message != WM_SYSKEYDOWN &&
4693          msg->message != WM_SYSKEYUP &&
4694          msg->message != WM_CHAR)) return 0;
4695
4696     TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4697                   "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4698                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4699
4700     i = 0;
4701     do
4702     {
4703         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4704                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4705             return 1;
4706     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4707     WARN_(accel)("couldn't translate accelerator key\n");
4708     return 0;
4709 }