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