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