Added more implementation of IDocumentView.
[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             DispatchMessageW( &msg );
2870         }
2871
2872         if (!fEndMenu) fRemove = TRUE;
2873
2874         /* finally remove message from the queue */
2875
2876         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2877             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2878         else mt.trackFlags &= ~TF_SKIPREMOVE;
2879     }
2880
2881     MENU_SetCapture(0);  /* release the capture */
2882
2883     /* If dropdown is still painted and the close box is clicked on
2884        then the menu will be destroyed as part of the DispatchMessage above.
2885        This will then invalidate the menu handle in mt.hTopMenu. We should
2886        check for this first.  */
2887     if( IsMenu( mt.hTopMenu ) )
2888     {
2889         menu = MENU_GetMenu( mt.hTopMenu );
2890
2891         if( IsWindow( mt.hOwnerWnd ) )
2892         {
2893             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2894
2895             if (menu && (menu->wFlags & MF_POPUP))
2896             {
2897                 DestroyWindow( menu->hWnd );
2898                 menu->hWnd = 0;
2899             }
2900             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2901             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2902         }
2903
2904         /* Reset the variable for hiding menu */
2905         if( menu ) menu->bTimeToHide = FALSE;
2906     }
2907
2908     /* The return value is only used by TrackPopupMenu */
2909     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2910     if (executedMenuId == -1) executedMenuId = 0;
2911     return executedMenuId;
2912 }
2913
2914 /***********************************************************************
2915  *           MENU_InitTracking
2916  */
2917 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2918 {
2919     POPUPMENU *menu;
2920     
2921     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2922
2923     HideCaret(0);
2924
2925     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2926     if (!(wFlags & TPM_NONOTIFY))
2927        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2928
2929     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2930
2931     if (!(wFlags & TPM_NONOTIFY))
2932     {
2933        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2934        /* If an app changed/recreated menu bar entries in WM_INITMENU
2935         * menu sizes will be recalculated once the menu created/shown.
2936         */
2937     }
2938     
2939     /* This makes the menus of applications built with Delphi work.
2940      * It also enables menus to be displayed in more than one window,
2941      * but there are some bugs left that need to be fixed in this case.
2942      */
2943     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
2944     
2945     return TRUE;
2946 }
2947 /***********************************************************************
2948  *           MENU_ExitTracking
2949  */
2950 static BOOL MENU_ExitTracking(HWND hWnd)
2951 {
2952     TRACE("hwnd=%p\n", hWnd);
2953
2954     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2955     ShowCaret(0);
2956     top_popup = 0;
2957     return TRUE;
2958 }
2959
2960 /***********************************************************************
2961  *           MENU_TrackMouseMenuBar
2962  *
2963  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2964  */
2965 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2966 {
2967     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2968     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2969
2970     TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2971
2972     if (IsMenu(hMenu))
2973     {
2974         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2975         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2976         MENU_ExitTracking(hWnd);
2977     }
2978 }
2979
2980
2981 /***********************************************************************
2982  *           MENU_TrackKbdMenuBar
2983  *
2984  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2985  */
2986 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
2987 {
2988     UINT uItem = NO_SELECTED_ITEM;
2989     HMENU hTrackMenu;
2990     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2991
2992     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
2993
2994     /* find window that has a menu */
2995
2996     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
2997         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
2998
2999     /* check if we have to track a system menu */
3000
3001     hTrackMenu = GetMenu( hwnd );
3002     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3003     {
3004         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3005         hTrackMenu = get_win_sys_menu( hwnd );
3006         uItem = 0;
3007         wParam |= HTSYSMENU; /* prevent item lookup */
3008     }
3009
3010     if (!IsMenu( hTrackMenu )) return;
3011
3012     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3013
3014     if( wChar && wChar != ' ' )
3015     {
3016         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3017         if ( uItem >= (UINT)(-2) )
3018         {
3019             if( uItem == (UINT)(-1) ) MessageBeep(0);
3020             /* schedule end of menu tracking */
3021             wFlags |= TF_ENDMENU;
3022             goto track_menu;
3023         }
3024     }
3025
3026     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3027
3028     if (wParam & HTSYSMENU)
3029     {
3030         /* prevent sysmenu activation for managed windows on Alt down/up */
3031         if (GetPropA( hwnd, "__wine_x11_managed" ))
3032             wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3033     }
3034     else
3035     {
3036         if( uItem == NO_SELECTED_ITEM )
3037             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3038         else
3039             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3040     }
3041
3042 track_menu:
3043     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3044     MENU_ExitTracking( hwnd );
3045 }
3046
3047
3048 /**********************************************************************
3049  *           TrackPopupMenu   (USER32.@)
3050  *
3051  * Like the win32 API, the function return the command ID only if the
3052  * flag TPM_RETURNCMD is on.
3053  *
3054  */
3055 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3056                            INT nReserved, HWND hWnd, const RECT *lpRect )
3057 {
3058     BOOL ret = FALSE;
3059
3060     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3061
3062     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3063     if (!(wFlags & TPM_NONOTIFY))
3064         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3065
3066     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3067         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3068     MENU_ExitTracking(hWnd);
3069
3070     return ret;
3071 }
3072
3073 /**********************************************************************
3074  *           TrackPopupMenuEx   (USER32.@)
3075  */
3076 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3077                                 HWND hWnd, LPTPMPARAMS lpTpm )
3078 {
3079     FIXME("not fully implemented\n" );
3080     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3081                              lpTpm ? &lpTpm->rcExclude : NULL );
3082 }
3083
3084 /***********************************************************************
3085  *           PopupMenuWndProc
3086  *
3087  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3088  */
3089 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3090 {
3091     TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3092
3093     switch(message)
3094     {
3095     case WM_CREATE:
3096         {
3097             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3098             SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3099             return 0;
3100         }
3101
3102     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3103         return MA_NOACTIVATE;
3104
3105     case WM_PAINT:
3106         {
3107             PAINTSTRUCT ps;
3108             BeginPaint( hwnd, &ps );
3109             MENU_DrawPopupMenu( hwnd, ps.hdc,
3110                                 (HMENU)GetWindowLongW( hwnd, 0 ) );
3111             EndPaint( hwnd, &ps );
3112             return 0;
3113         }
3114     case WM_ERASEBKGND:
3115         return 1;
3116
3117     case WM_DESTROY:
3118         /* zero out global pointer in case resident popup window was destroyed. */
3119         if (hwnd == top_popup) top_popup = 0;
3120         break;
3121
3122     case WM_SHOWWINDOW:
3123
3124         if( wParam )
3125         {
3126             if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3127         }
3128         else
3129             SetWindowLongW( hwnd, 0, 0 );
3130         break;
3131
3132     case MM_SETMENUHANDLE:
3133         SetWindowLongW( hwnd, 0, wParam );
3134         break;
3135
3136     case MM_GETMENUHANDLE:
3137         return GetWindowLongW( hwnd, 0 );
3138
3139     default:
3140         return DefWindowProcW( hwnd, message, wParam, lParam );
3141     }
3142     return 0;
3143 }
3144
3145
3146 /***********************************************************************
3147  *           MENU_GetMenuBarHeight
3148  *
3149  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3150  */
3151 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3152                               INT orgX, INT orgY )
3153 {
3154     HDC hdc;
3155     RECT rectBar;
3156     LPPOPUPMENU lppop;
3157
3158     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3159
3160     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3161
3162     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3163     SelectObject( hdc, hMenuFont);
3164     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3165     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3166     ReleaseDC( hwnd, hdc );
3167     return lppop->Height;
3168 }
3169
3170
3171 /*******************************************************************
3172  *         ChangeMenuA    (USER32.@)
3173  */
3174 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3175                              UINT id, UINT flags )
3176 {
3177     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3178     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3179                                                  id, data );
3180     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3181     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3182                                                 id, data );
3183     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3184                                               flags & MF_BYPOSITION ? pos : id,
3185                                               flags & ~MF_REMOVE );
3186     /* Default: MF_INSERT */
3187     return InsertMenuA( hMenu, pos, flags, id, data );
3188 }
3189
3190
3191 /*******************************************************************
3192  *         ChangeMenuW    (USER32.@)
3193  */
3194 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3195                              UINT id, UINT flags )
3196 {
3197     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3198     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3199                                                  id, data );
3200     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3201     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3202                                                 id, data );
3203     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3204                                               flags & MF_BYPOSITION ? pos : id,
3205                                               flags & ~MF_REMOVE );
3206     /* Default: MF_INSERT */
3207     return InsertMenuW( hMenu, pos, flags, id, data );
3208 }
3209
3210
3211 /*******************************************************************
3212  *         CheckMenuItem    (USER32.@)
3213  */
3214 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3215 {
3216     MENUITEM *item;
3217     DWORD ret;
3218
3219     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3220     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3221     ret = item->fState & MF_CHECKED;
3222     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3223     else item->fState &= ~MF_CHECKED;
3224     return ret;
3225 }
3226
3227
3228 /**********************************************************************
3229  *         EnableMenuItem    (USER32.@)
3230  */
3231 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3232 {
3233     UINT    oldflags;
3234     MENUITEM *item;
3235     POPUPMENU *menu;
3236
3237     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3238
3239     /* Get the Popupmenu to access the owner menu */
3240     if (!(menu = MENU_GetMenu(hMenu)))
3241         return (UINT)-1;
3242
3243     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3244         return (UINT)-1;
3245
3246     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3247     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3248
3249     /* If the close item in the system menu change update the close button */
3250     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3251     {
3252         if (menu->hSysMenuOwner != 0)
3253         {
3254             RECT rc;
3255             POPUPMENU* parentMenu;
3256
3257             /* Get the parent menu to access*/
3258             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3259                 return (UINT)-1;
3260
3261             /* Refresh the frame to reflect the change */
3262             GetWindowRect(parentMenu->hWnd, &rc);
3263             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3264             rc.bottom = 0;
3265             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3266         }
3267     }
3268
3269     return oldflags;
3270 }
3271
3272
3273 /*******************************************************************
3274  *         GetMenuStringA    (USER32.@)
3275  */
3276 INT WINAPI GetMenuStringA(
3277         HMENU hMenu,    /* [in] menuhandle */
3278         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3279         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3280         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3281         UINT wFlags     /* [in] MF_ flags */
3282 ) {
3283     MENUITEM *item;
3284
3285     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3286     if (str && nMaxSiz) str[0] = '\0';
3287     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3288     if (!IS_STRING_ITEM(item->fType)) return 0;
3289     if (!str || !nMaxSiz) return strlenW(item->text);
3290     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3291         str[nMaxSiz-1] = 0;
3292     TRACE("returning '%s'\n", str );
3293     return strlen(str);
3294 }
3295
3296
3297 /*******************************************************************
3298  *         GetMenuStringW    (USER32.@)
3299  */
3300 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3301                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3302 {
3303     MENUITEM *item;
3304
3305     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3306     if (str && nMaxSiz) str[0] = '\0';
3307     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3308     if (!IS_STRING_ITEM(item->fType)) return 0;
3309     if (!str || !nMaxSiz) return strlenW(item->text);
3310     lstrcpynW( str, item->text, nMaxSiz );
3311     return strlenW(str);
3312 }
3313
3314
3315 /**********************************************************************
3316  *         HiliteMenuItem    (USER32.@)
3317  */
3318 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3319                                 UINT wHilite )
3320 {
3321     LPPOPUPMENU menu;
3322     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3323     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3324     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3325     if (menu->FocusedItem == wItemID) return TRUE;
3326     MENU_HideSubPopups( hWnd, hMenu, FALSE );
3327     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3328     return TRUE;
3329 }
3330
3331
3332 /**********************************************************************
3333  *         GetMenuState    (USER32.@)
3334  */
3335 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3336 {
3337     MENUITEM *item;
3338     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3339     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3340     debug_print_menuitem ("  item: ", item, "");
3341     if (item->fType & MF_POPUP)
3342     {
3343         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3344         if (!menu) return -1;
3345         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3346     }
3347     else
3348     {
3349         /* We used to (from way back then) mask the result to 0xff.  */
3350         /* I don't know why and it seems wrong as the documented */
3351         /* return flag MF_SEPARATOR is outside that mask.  */
3352         return (item->fType | item->fState);
3353     }
3354 }
3355
3356
3357 /**********************************************************************
3358  *         GetMenuItemCount    (USER32.@)
3359  */
3360 INT WINAPI GetMenuItemCount( HMENU hMenu )
3361 {
3362     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3363     if (!menu) return -1;
3364     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3365     return menu->nItems;
3366 }
3367
3368
3369 /**********************************************************************
3370  *         GetMenuItemID    (USER32.@)
3371  */
3372 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3373 {
3374     MENUITEM * lpmi;
3375
3376     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3377     if (lpmi->fType & MF_POPUP) return -1;
3378     return lpmi->wID;
3379
3380 }
3381
3382
3383 /*******************************************************************
3384  *         InsertMenuW    (USER32.@)
3385  */
3386 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3387                          UINT_PTR id, LPCWSTR str )
3388 {
3389     MENUITEM *item;
3390
3391     if (IS_STRING_ITEM(flags) && str)
3392         TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3393               hMenu, pos, flags, id, debugstr_w(str) );
3394     else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3395                hMenu, pos, flags, id, (DWORD)str );
3396
3397     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3398
3399     if (!(MENU_SetItemData( item, flags, id, str )))
3400     {
3401         RemoveMenu( hMenu, pos, flags );
3402         return FALSE;
3403     }
3404
3405     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3406         (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3407
3408     item->hCheckBit = item->hUnCheckBit = 0;
3409     return TRUE;
3410 }
3411
3412
3413 /*******************************************************************
3414  *         InsertMenuA    (USER32.@)
3415  */
3416 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3417                          UINT_PTR id, LPCSTR str )
3418 {
3419     BOOL ret = FALSE;
3420
3421     if (IS_STRING_ITEM(flags) && str)
3422     {
3423         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3424         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3425         if (newstr)
3426         {
3427             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3428             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3429             HeapFree( GetProcessHeap(), 0, newstr );
3430         }
3431         return ret;
3432     }
3433     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3434 }
3435
3436
3437 /*******************************************************************
3438  *         AppendMenuA    (USER32.@)
3439  */
3440 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3441                          UINT_PTR id, LPCSTR data )
3442 {
3443     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3444 }
3445
3446
3447 /*******************************************************************
3448  *         AppendMenuW    (USER32.@)
3449  */
3450 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3451                          UINT_PTR id, LPCWSTR data )
3452 {
3453     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3454 }
3455
3456
3457 /**********************************************************************
3458  *         RemoveMenu    (USER32.@)
3459  */
3460 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3461 {
3462     LPPOPUPMENU menu;
3463     MENUITEM *item;
3464
3465     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3466     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3467     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3468
3469       /* Remove item */
3470
3471     MENU_FreeItemData( item );
3472
3473     if (--menu->nItems == 0)
3474     {
3475         HeapFree( GetProcessHeap(), 0, menu->items );
3476         menu->items = NULL;
3477     }
3478     else
3479     {
3480         while(nPos < menu->nItems)
3481         {
3482             *item = *(item+1);
3483             item++;
3484             nPos++;
3485         }
3486         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3487                                    menu->nItems * sizeof(MENUITEM) );
3488     }
3489     return TRUE;
3490 }
3491
3492
3493 /**********************************************************************
3494  *         DeleteMenu    (USER32.@)
3495  */
3496 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3497 {
3498     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3499     if (!item) return FALSE;
3500     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3501       /* nPos is now the position of the item */
3502     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3503     return TRUE;
3504 }
3505
3506
3507 /*******************************************************************
3508  *         ModifyMenuW    (USER32.@)
3509  */
3510 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3511                          UINT_PTR id, LPCWSTR str )
3512 {
3513     MENUITEM *item;
3514
3515     if (IS_STRING_ITEM(flags))
3516     {
3517         TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3518     }
3519     else
3520     {
3521         TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3522     }
3523
3524     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3525     return MENU_SetItemData( item, flags, id, str );
3526 }
3527
3528
3529 /*******************************************************************
3530  *         ModifyMenuA    (USER32.@)
3531  */
3532 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3533                          UINT_PTR id, LPCSTR str )
3534 {
3535     BOOL ret = FALSE;
3536
3537     if (IS_STRING_ITEM(flags) && str)
3538     {
3539         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3540         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3541         if (newstr)
3542         {
3543             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3544             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3545             HeapFree( GetProcessHeap(), 0, newstr );
3546         }
3547         return ret;
3548     }
3549     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3550 }
3551
3552
3553 /**********************************************************************
3554  *         CreatePopupMenu    (USER32.@)
3555  */
3556 HMENU WINAPI CreatePopupMenu(void)
3557 {
3558     HMENU hmenu;
3559     POPUPMENU *menu;
3560
3561     if (!(hmenu = CreateMenu())) return 0;
3562     menu = MENU_GetMenu( hmenu );
3563     menu->wFlags |= MF_POPUP;
3564     menu->bTimeToHide = FALSE;
3565     return hmenu;
3566 }
3567
3568
3569 /**********************************************************************
3570  *         GetMenuCheckMarkDimensions    (USER.417)
3571  *         GetMenuCheckMarkDimensions    (USER32.@)
3572  */
3573 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3574 {
3575     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3576 }
3577
3578
3579 /**********************************************************************
3580  *         SetMenuItemBitmaps    (USER32.@)
3581  */
3582 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3583                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3584 {
3585     MENUITEM *item;
3586     TRACE("(%p, %04x, %04x, %p, %p)\n",
3587           hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3588     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3589
3590     if (!hNewCheck && !hNewUnCheck)
3591     {
3592         item->fState &= ~MF_USECHECKBITMAPS;
3593     }
3594     else  /* Install new bitmaps */
3595     {
3596         item->hCheckBit = hNewCheck;
3597         item->hUnCheckBit = hNewUnCheck;
3598         item->fState |= MF_USECHECKBITMAPS;
3599     }
3600     return TRUE;
3601 }
3602
3603
3604 /**********************************************************************
3605  *         CreateMenu    (USER32.@)
3606  */
3607 HMENU WINAPI CreateMenu(void)
3608 {
3609     HMENU hMenu;
3610     LPPOPUPMENU menu;
3611     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3612     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3613
3614     ZeroMemory(menu, sizeof(POPUPMENU));
3615     menu->wMagic = MENU_MAGIC;
3616     menu->FocusedItem = NO_SELECTED_ITEM;
3617     menu->bTimeToHide = FALSE;
3618
3619     TRACE("return %p\n", hMenu );
3620
3621     return hMenu;
3622 }
3623
3624
3625 /**********************************************************************
3626  *         DestroyMenu    (USER32.@)
3627  */
3628 BOOL WINAPI DestroyMenu( HMENU hMenu )
3629 {
3630     TRACE("(%p)\n", hMenu);
3631
3632     /* Silently ignore attempts to destroy default system popup */
3633
3634     if (hMenu && hMenu != MENU_DefSysPopup)
3635     {
3636         LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3637
3638         if (!lppop) return FALSE;
3639
3640         lppop->wMagic = 0;  /* Mark it as destroyed */
3641
3642         /* DestroyMenu should not destroy system menu popup owner */
3643         if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3644         {
3645             DestroyWindow( lppop->hWnd );
3646             lppop->hWnd = 0;
3647         }
3648
3649         if (lppop->items) /* recursively destroy submenus */
3650         {
3651             int i;
3652             MENUITEM *item = lppop->items;
3653             for (i = lppop->nItems; i > 0; i--, item++)
3654             {
3655                 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3656                 MENU_FreeItemData( item );
3657             }
3658             HeapFree( GetProcessHeap(), 0, lppop->items );
3659         }
3660         USER_HEAP_FREE( hMenu );
3661     }
3662     return (hMenu != MENU_DefSysPopup);
3663 }
3664
3665
3666 /**********************************************************************
3667  *         GetSystemMenu    (USER32.@)
3668  */
3669 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3670 {
3671     WND *wndPtr = WIN_GetPtr( hWnd );
3672     HMENU retvalue = 0;
3673
3674     if (wndPtr == WND_DESKTOP) return 0;
3675     if (wndPtr == WND_OTHER_PROCESS)
3676     {
3677         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3678     }
3679     else if (wndPtr)
3680     {
3681         if( wndPtr->hSysMenu )
3682         {
3683             if( bRevert )
3684             {
3685                 DestroyMenu(wndPtr->hSysMenu);
3686                 wndPtr->hSysMenu = 0;
3687             }
3688             else
3689             {
3690                 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3691                 if( menu )
3692                 {
3693                    if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3694                       menu->items[0].hSubMenu = MENU_CopySysPopup();
3695                 }
3696                 else
3697                 {
3698                    WARN("Current sys-menu (%p) of wnd %p is broken\n",
3699                         wndPtr->hSysMenu, hWnd);
3700                    wndPtr->hSysMenu = 0;
3701                 }
3702             }
3703         }
3704
3705         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3706             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3707
3708         if( wndPtr->hSysMenu )
3709         {
3710             POPUPMENU *menu;
3711             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3712
3713             /* Store the dummy sysmenu handle to facilitate the refresh */
3714             /* of the close button if the SC_CLOSE item change */
3715             menu = MENU_GetMenu(retvalue);
3716             if ( menu )
3717                menu->hSysMenuOwner = wndPtr->hSysMenu;
3718         }
3719         WIN_ReleasePtr( wndPtr );
3720     }
3721     return bRevert ? 0 : retvalue;
3722 }
3723
3724
3725 /*******************************************************************
3726  *         SetSystemMenu    (USER32.@)
3727  */
3728 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3729 {
3730     WND *wndPtr = WIN_GetPtr( hwnd );
3731
3732     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
3733     {
3734         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3735         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3736         WIN_ReleasePtr( wndPtr );
3737         return TRUE;
3738     }
3739     return FALSE;
3740 }
3741
3742
3743 /**********************************************************************
3744  *         GetMenu    (USER32.@)
3745  */
3746 HMENU WINAPI GetMenu( HWND hWnd )
3747 {
3748     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
3749     TRACE("for %p returning %p\n", hWnd, retvalue);
3750     return retvalue;
3751 }
3752
3753
3754 /**********************************************************************
3755  *         MENU_SetMenu
3756  *
3757  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
3758  * SetWindowPos call that would result if SetMenu were called directly.
3759  */
3760 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
3761 {
3762     TRACE("(%p, %p);\n", hWnd, hMenu);
3763
3764     if (hMenu && !IsMenu(hMenu))
3765     {
3766         WARN("hMenu %p is not a menu handle\n", hMenu);
3767         return FALSE;
3768     }
3769     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3770         return FALSE;
3771
3772     hWnd = WIN_GetFullHandle( hWnd );
3773     if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
3774
3775     if (hMenu != 0)
3776     {
3777         LPPOPUPMENU lpmenu;
3778
3779         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3780
3781         lpmenu->hWnd = hWnd;
3782         lpmenu->Height = 0;  /* Make sure we recalculate the size */
3783     }
3784     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
3785     return TRUE;
3786 }
3787
3788
3789 /**********************************************************************
3790  *         SetMenu    (USER32.@)
3791  */
3792 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3793 {   
3794     if(!MENU_SetMenu(hWnd, hMenu))
3795         return FALSE;
3796  
3797     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3798                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3799     return TRUE;
3800 }
3801
3802
3803 /**********************************************************************
3804  *         GetSubMenu    (USER32.@)
3805  */
3806 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3807 {
3808     MENUITEM * lpmi;
3809
3810     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3811     if (!(lpmi->fType & MF_POPUP)) return 0;
3812     return lpmi->hSubMenu;
3813 }
3814
3815
3816 /**********************************************************************
3817  *         DrawMenuBar    (USER32.@)
3818  */
3819 BOOL WINAPI DrawMenuBar( HWND hWnd )
3820 {
3821     LPPOPUPMENU lppop;
3822     HMENU hMenu = GetMenu(hWnd);
3823
3824     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
3825         return FALSE;
3826     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3827
3828     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3829     lppop->hwndOwner = hWnd;
3830     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3831                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3832     return TRUE;
3833 }
3834
3835 /***********************************************************************
3836  *           DrawMenuBarTemp   (USER32.@)
3837  *
3838  * UNDOCUMENTED !!
3839  *
3840  * called by W98SE desk.cpl Control Panel Applet
3841  *
3842  * Not 100% sure about the param names, but close.
3843  */
3844 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3845 {
3846     LPPOPUPMENU lppop;
3847     UINT i,retvalue;
3848     HFONT hfontOld = 0;
3849
3850     if (!hMenu)
3851         hMenu = GetMenu(hwnd);
3852
3853     if (!hFont)
3854         hFont = hMenuFont;
3855
3856     lppop = MENU_GetMenu( hMenu );
3857     if (lppop == NULL || lprect == NULL)
3858     {
3859         retvalue = GetSystemMetrics(SM_CYMENU);
3860         goto END;
3861     }
3862
3863     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3864
3865     hfontOld = SelectObject( hDC, hFont);
3866
3867     if (lppop->Height == 0)
3868         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3869
3870     lprect->bottom = lprect->top + lppop->Height;
3871
3872     FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3873
3874     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3875     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3876     LineTo( hDC, lprect->right, lprect->bottom );
3877
3878     if (lppop->nItems == 0)
3879     {
3880         retvalue = GetSystemMetrics(SM_CYMENU);
3881         goto END;
3882     }
3883
3884     for (i = 0; i < lppop->nItems; i++)
3885     {
3886         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3887                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3888     }
3889     retvalue = lppop->Height;
3890
3891 END:
3892     if (hfontOld) SelectObject (hDC, hfontOld);
3893     return retvalue;
3894 }
3895
3896 /***********************************************************************
3897  *           EndMenu   (USER.187)
3898  *           EndMenu   (USER32.@)
3899  */
3900 void WINAPI EndMenu(void)
3901 {
3902     /* if we are in the menu code, and it is active */
3903     if (!fEndMenu && top_popup)
3904     {
3905         /* terminate the menu handling code */
3906         fEndMenu = TRUE;
3907
3908         /* needs to be posted to wakeup the internal menu handler */
3909         /* which will now terminate the menu, in the event that */
3910         /* the main window was minimized, or lost focus, so we */
3911         /* don't end up with an orphaned menu */
3912         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3913     }
3914 }
3915
3916
3917 /***********************************************************************
3918  *           LookupMenuHandle   (USER.217)
3919  */
3920 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3921 {
3922     HMENU hmenu32 = HMENU_32(hmenu);
3923     UINT id32 = id;
3924     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3925     else return HMENU_16(hmenu32);
3926 }
3927
3928
3929 /**********************************************************************
3930  *          LoadMenu    (USER.150)
3931  */
3932 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3933 {
3934     HRSRC16 hRsrc;
3935     HGLOBAL16 handle;
3936     HMENU16 hMenu;
3937
3938     if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3939     if (!name) return 0;
3940
3941     instance = GetExePtr( instance );
3942     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3943     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3944     hMenu = LoadMenuIndirect16(LockResource16(handle));
3945     FreeResource16( handle );
3946     return hMenu;
3947 }
3948
3949
3950 /*****************************************************************
3951  *        LoadMenuA   (USER32.@)
3952  */
3953 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3954 {
3955     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3956     if (!hrsrc) return 0;
3957     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3958 }
3959
3960
3961 /*****************************************************************
3962  *        LoadMenuW   (USER32.@)
3963  */
3964 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3965 {
3966     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3967     if (!hrsrc) return 0;
3968     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3969 }
3970
3971
3972 /**********************************************************************
3973  *          LoadMenuIndirect    (USER.220)
3974  */
3975 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3976 {
3977     HMENU hMenu;
3978     WORD version, offset;
3979     LPCSTR p = (LPCSTR)template;
3980
3981     TRACE("(%p)\n", template );
3982     version = GET_WORD(p);
3983     p += sizeof(WORD);
3984     if (version)
3985     {
3986         WARN("version must be 0 for Win16\n" );
3987         return 0;
3988     }
3989     offset = GET_WORD(p);
3990     p += sizeof(WORD) + offset;
3991     if (!(hMenu = CreateMenu())) return 0;
3992     if (!MENU_ParseResource( p, hMenu, FALSE ))
3993     {
3994         DestroyMenu( hMenu );
3995         return 0;
3996     }
3997     return HMENU_16(hMenu);
3998 }
3999
4000
4001 /**********************************************************************
4002  *          LoadMenuIndirectW    (USER32.@)
4003  */
4004 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4005 {
4006     HMENU hMenu;
4007     WORD version, offset;
4008     LPCSTR p = (LPCSTR)template;
4009
4010     version = GET_WORD(p);
4011     p += sizeof(WORD);
4012     TRACE("%p, ver %d\n", template, version );
4013     switch (version)
4014     {
4015       case 0: /* standard format is version of 0 */
4016         offset = GET_WORD(p);
4017         p += sizeof(WORD) + offset;
4018         if (!(hMenu = CreateMenu())) return 0;
4019         if (!MENU_ParseResource( p, hMenu, TRUE ))
4020           {
4021             DestroyMenu( hMenu );
4022             return 0;
4023           }
4024         return hMenu;
4025       case 1: /* extended format is version of 1 */
4026         offset = GET_WORD(p);
4027         p += sizeof(WORD) + offset;
4028         if (!(hMenu = CreateMenu())) return 0;
4029         if (!MENUEX_ParseResource( p, hMenu))
4030           {
4031             DestroyMenu( hMenu );
4032             return 0;
4033           }
4034         return hMenu;
4035       default:
4036         ERR("version %d not supported.\n", version);
4037         return 0;
4038     }
4039 }
4040
4041
4042 /**********************************************************************
4043  *          LoadMenuIndirectA    (USER32.@)
4044  */
4045 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4046 {
4047     return LoadMenuIndirectW( template );
4048 }
4049
4050
4051 /**********************************************************************
4052  *              IsMenu    (USER32.@)
4053  */
4054 BOOL WINAPI IsMenu(HMENU hmenu)
4055 {
4056     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4057     return menu != NULL;
4058 }
4059
4060 /**********************************************************************
4061  *              GetMenuItemInfo_common
4062  */
4063
4064 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4065                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4066 {
4067     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4068
4069     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4070
4071     if (!menu)
4072         return FALSE;
4073
4074     if (lpmii->fMask & MIIM_TYPE) {
4075         lpmii->fType = menu->fType;
4076         switch (MENU_ITEM_TYPE(menu->fType)) {
4077         case MF_STRING:
4078             break;  /* will be done below */
4079         case MF_OWNERDRAW:
4080         case MF_BITMAP:
4081             lpmii->dwTypeData = menu->text;
4082             /* fall through */
4083         default:
4084             lpmii->cch = 0;
4085         }
4086     }
4087
4088     /* copy the text string */
4089     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4090          (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4091     {
4092         int len;
4093         if (unicode)
4094         {
4095             len = strlenW(menu->text);
4096             if(lpmii->dwTypeData && lpmii->cch)
4097                 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4098         }
4099         else
4100         {
4101             len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4102             if(lpmii->dwTypeData && lpmii->cch)
4103                 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4104                                           (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4105                     ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4106         }
4107         /* if we've copied a substring we return its length */
4108         if(lpmii->dwTypeData && lpmii->cch)
4109         {
4110             if (lpmii->cch <= len) lpmii->cch--;
4111         }
4112         else /* return length of string */
4113             lpmii->cch = len;
4114     }
4115
4116     if (lpmii->fMask & MIIM_FTYPE)
4117         lpmii->fType = menu->fType;
4118
4119     if (lpmii->fMask & MIIM_BITMAP)
4120         lpmii->hbmpItem = menu->hbmpItem;
4121
4122     if (lpmii->fMask & MIIM_STATE)
4123         lpmii->fState = menu->fState;
4124
4125     if (lpmii->fMask & MIIM_ID)
4126         lpmii->wID = menu->wID;
4127
4128     if (lpmii->fMask & MIIM_SUBMENU)
4129         lpmii->hSubMenu = menu->hSubMenu;
4130
4131     if (lpmii->fMask & MIIM_CHECKMARKS) {
4132         lpmii->hbmpChecked = menu->hCheckBit;
4133         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4134     }
4135     if (lpmii->fMask & MIIM_DATA)
4136         lpmii->dwItemData = menu->dwItemData;
4137
4138   return TRUE;
4139 }
4140
4141 /**********************************************************************
4142  *              GetMenuItemInfoA    (USER32.@)
4143  */
4144 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4145                                   LPMENUITEMINFOA lpmii)
4146 {
4147     return GetMenuItemInfo_common (hmenu, item, bypos,
4148                                     (LPMENUITEMINFOW)lpmii, FALSE);
4149 }
4150
4151 /**********************************************************************
4152  *              GetMenuItemInfoW    (USER32.@)
4153  */
4154 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4155                                   LPMENUITEMINFOW lpmii)
4156 {
4157     return GetMenuItemInfo_common (hmenu, item, bypos,
4158                                      lpmii, TRUE);
4159 }
4160
4161
4162 /* set a menu item text from a ASCII or Unicode string */
4163 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4164 {
4165     if (!text)
4166     {
4167         menu->text = NULL;
4168         menu->fType |= MF_SEPARATOR;
4169     }
4170     else if (unicode)
4171     {
4172         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4173             strcpyW( menu->text, text );
4174     }
4175     else
4176     {
4177         LPCSTR str = (LPCSTR)text;
4178         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4179         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4180             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4181     }
4182 }
4183
4184
4185 /**********************************************************************
4186  *              SetMenuItemInfo_common
4187  */
4188
4189 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4190                                        const MENUITEMINFOW *lpmii,
4191                                        BOOL unicode)
4192 {
4193     if (!menu) return FALSE;
4194
4195     debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4196
4197     if (lpmii->fMask & MIIM_TYPE ) {
4198         /* Get rid of old string. */
4199         if (IS_STRING_ITEM(menu->fType) && menu->text) {
4200             HeapFree(GetProcessHeap(), 0, menu->text);
4201             menu->text = NULL;
4202         }
4203
4204         /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4205         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4206         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4207
4208         menu->text = lpmii->dwTypeData;
4209
4210        if (IS_STRING_ITEM(menu->fType))
4211            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4212     }
4213
4214     if (lpmii->fMask & MIIM_FTYPE ) {
4215         /* free the string when the type is changing */
4216         if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4217             HeapFree(GetProcessHeap(), 0, menu->text);
4218             menu->text = NULL;
4219         }
4220         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4221         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4222         if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4223             menu->fType |= MF_SEPARATOR;
4224     }
4225
4226     if (lpmii->fMask & MIIM_STRING ) {
4227         if (IS_STRING_ITEM(menu->fType)) {
4228             /* free the string when used */
4229             HeapFree(GetProcessHeap(), 0, menu->text);
4230             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4231         }
4232     }
4233
4234     if (lpmii->fMask & MIIM_STATE)
4235     {
4236         /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4237         menu->fState = lpmii->fState;
4238     }
4239
4240     if (lpmii->fMask & MIIM_ID)
4241         menu->wID = lpmii->wID;
4242
4243     if (lpmii->fMask & MIIM_SUBMENU) {
4244         menu->hSubMenu = lpmii->hSubMenu;
4245         if (menu->hSubMenu) {
4246             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4247             if (subMenu) {
4248                 subMenu->wFlags |= MF_POPUP;
4249                 menu->fType |= MF_POPUP;
4250             }
4251             else
4252                 /* FIXME: Return an error ? */
4253                 menu->fType &= ~MF_POPUP;
4254         }
4255         else
4256             menu->fType &= ~MF_POPUP;
4257     }
4258
4259     if (lpmii->fMask & MIIM_CHECKMARKS)
4260     {
4261         if (lpmii->fType & MFT_RADIOCHECK)
4262             menu->fType |= MFT_RADIOCHECK;
4263
4264         menu->hCheckBit = lpmii->hbmpChecked;
4265         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4266     }
4267     if (lpmii->fMask & MIIM_DATA)
4268         menu->dwItemData = lpmii->dwItemData;
4269
4270     if (lpmii->fMask & MIIM_BITMAP)
4271         menu->hbmpItem = lpmii->hbmpItem;
4272
4273     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4274     return TRUE;
4275 }
4276
4277 /**********************************************************************
4278  *              SetMenuItemInfoA    (USER32.@)
4279  */
4280 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4281                                  const MENUITEMINFOA *lpmii)
4282 {
4283     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4284                                     (const MENUITEMINFOW *)lpmii, FALSE);
4285 }
4286
4287 /**********************************************************************
4288  *              SetMenuItemInfoW    (USER32.@)
4289  */
4290 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4291                                  const MENUITEMINFOW *lpmii)
4292 {
4293     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4294                                     lpmii, TRUE);
4295 }
4296
4297 /**********************************************************************
4298  *              SetMenuDefaultItem    (USER32.@)
4299  *
4300  */
4301 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4302 {
4303         UINT i;
4304         POPUPMENU *menu;
4305         MENUITEM *item;
4306
4307         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4308
4309         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4310
4311         /* reset all default-item flags */
4312         item = menu->items;
4313         for (i = 0; i < menu->nItems; i++, item++)
4314         {
4315             item->fState &= ~MFS_DEFAULT;
4316         }
4317
4318         /* no default item */
4319         if ( -1 == uItem)
4320         {
4321             return TRUE;
4322         }
4323
4324         item = menu->items;
4325         if ( bypos )
4326         {
4327             if ( uItem >= menu->nItems ) return FALSE;
4328             item[uItem].fState |= MFS_DEFAULT;
4329             return TRUE;
4330         }
4331         else
4332         {
4333             for (i = 0; i < menu->nItems; i++, item++)
4334             {
4335                 if (item->wID == uItem)
4336                 {
4337                      item->fState |= MFS_DEFAULT;
4338                      return TRUE;
4339                 }
4340             }
4341
4342         }
4343         return FALSE;
4344 }
4345
4346 /**********************************************************************
4347  *              GetMenuDefaultItem    (USER32.@)
4348  */
4349 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4350 {
4351         POPUPMENU *menu;
4352         MENUITEM * item;
4353         UINT i = 0;
4354
4355         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4356
4357         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4358
4359         /* find default item */
4360         item = menu->items;
4361
4362         /* empty menu */
4363         if (! item) return -1;
4364
4365         while ( !( item->fState & MFS_DEFAULT ) )
4366         {
4367             i++; item++;
4368             if  (i >= menu->nItems ) return -1;
4369         }
4370
4371         /* default: don't return disabled items */
4372         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4373
4374         /* search rekursiv when needed */
4375         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4376         {
4377             UINT ret;
4378             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4379             if ( -1 != ret ) return ret;
4380
4381             /* when item not found in submenu, return the popup item */
4382         }
4383         return ( bypos ) ? i : item->wID;
4384
4385 }
4386
4387
4388 /**********************************************************************
4389  *              InsertMenuItemA    (USER32.@)
4390  */
4391 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4392                                 const MENUITEMINFOA *lpmii)
4393 {
4394     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4395     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4396 }
4397
4398
4399 /**********************************************************************
4400  *              InsertMenuItemW    (USER32.@)
4401  */
4402 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4403                                 const MENUITEMINFOW *lpmii)
4404 {
4405     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4406     return SetMenuItemInfo_common(item, lpmii, TRUE);
4407 }
4408
4409 /**********************************************************************
4410  *              CheckMenuRadioItem    (USER32.@)
4411  */
4412
4413 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4414                                    UINT first, UINT last, UINT check,
4415                                    UINT bypos)
4416 {
4417      MENUITEM *mifirst, *milast, *micheck;
4418      HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4419
4420      TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4421
4422      mifirst = MENU_FindItem (&mfirst, &first, bypos);
4423      milast = MENU_FindItem (&mlast, &last, bypos);
4424      micheck = MENU_FindItem (&mcheck, &check, bypos);
4425
4426      if (mifirst == NULL || milast == NULL || micheck == NULL ||
4427          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4428          micheck > milast || micheck < mifirst)
4429           return FALSE;
4430
4431      while (mifirst <= milast)
4432      {
4433           if (mifirst == micheck)
4434           {
4435                mifirst->fType |= MFT_RADIOCHECK;
4436                mifirst->fState |= MFS_CHECKED;
4437           } else {
4438                mifirst->fType &= ~MFT_RADIOCHECK;
4439                mifirst->fState &= ~MFS_CHECKED;
4440           }
4441           mifirst++;
4442      }
4443
4444      return TRUE;
4445 }
4446
4447
4448 /**********************************************************************
4449  *              GetMenuItemRect    (USER32.@)
4450  *
4451  *      ATTENTION: Here, the returned values in rect are the screen
4452  *                 coordinates of the item just like if the menu was
4453  *                 always on the upper left side of the application.
4454  *
4455  */
4456 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4457                                  LPRECT rect)
4458 {
4459      POPUPMENU *itemMenu;
4460      MENUITEM *item;
4461      HWND referenceHwnd;
4462
4463      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4464
4465      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4466      referenceHwnd = hwnd;
4467
4468      if(!hwnd)
4469      {
4470          itemMenu = MENU_GetMenu(hMenu);
4471          if (itemMenu == NULL)
4472              return FALSE;
4473
4474          if(itemMenu->hWnd == 0)
4475              return FALSE;
4476          referenceHwnd = itemMenu->hWnd;
4477      }
4478
4479      if ((rect == NULL) || (item == NULL))
4480          return FALSE;
4481
4482      *rect = item->rect;
4483
4484      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4485
4486      return TRUE;
4487 }
4488
4489
4490 /**********************************************************************
4491  *              SetMenuInfo    (USER32.@)
4492  *
4493  * FIXME
4494  *      MIM_APPLYTOSUBMENUS
4495  *      actually use the items to draw the menu
4496  */
4497 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4498 {
4499     POPUPMENU *menu;
4500
4501     TRACE("(%p %p)\n", hMenu, lpmi);
4502
4503     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4504     {
4505
4506         if (lpmi->fMask & MIM_BACKGROUND)
4507             menu->hbrBack = lpmi->hbrBack;
4508
4509         if (lpmi->fMask & MIM_HELPID)
4510             menu->dwContextHelpID = lpmi->dwContextHelpID;
4511
4512         if (lpmi->fMask & MIM_MAXHEIGHT)
4513             menu->cyMax = lpmi->cyMax;
4514
4515         if (lpmi->fMask & MIM_MENUDATA)
4516             menu->dwMenuData = lpmi->dwMenuData;
4517
4518         if (lpmi->fMask & MIM_STYLE)
4519         {
4520             menu->dwStyle = lpmi->dwStyle;
4521             if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4522             if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4523             if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4524             if (menu->dwStyle & MNS_NOCHECK) FIXME("MNS_NOCHECK unimplemented\n");
4525             if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4526         }
4527
4528         return TRUE;
4529     }
4530     return FALSE;
4531 }
4532
4533 /**********************************************************************
4534  *              GetMenuInfo    (USER32.@)
4535  *
4536  *  NOTES
4537  *      win98/NT5.0
4538  *
4539  */
4540 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4541 {   POPUPMENU *menu;
4542
4543     TRACE("(%p %p)\n", hMenu, lpmi);
4544
4545     if (lpmi && (menu = MENU_GetMenu(hMenu)))
4546     {
4547
4548         if (lpmi->fMask & MIM_BACKGROUND)
4549             lpmi->hbrBack = menu->hbrBack;
4550
4551         if (lpmi->fMask & MIM_HELPID)
4552             lpmi->dwContextHelpID = menu->dwContextHelpID;
4553
4554         if (lpmi->fMask & MIM_MAXHEIGHT)
4555             lpmi->cyMax = menu->cyMax;
4556
4557         if (lpmi->fMask & MIM_MENUDATA)
4558             lpmi->dwMenuData = menu->dwMenuData;
4559
4560         if (lpmi->fMask & MIM_STYLE)
4561             lpmi->dwStyle = menu->dwStyle;
4562
4563         return TRUE;
4564     }
4565     return FALSE;
4566 }
4567
4568
4569 /**********************************************************************
4570  *         SetMenuContextHelpId    (USER32.@)
4571  */
4572 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4573 {
4574     LPPOPUPMENU menu;
4575
4576     TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4577
4578     if ((menu = MENU_GetMenu(hMenu)))
4579     {
4580         menu->dwContextHelpID = dwContextHelpID;
4581         return TRUE;
4582     }
4583     return FALSE;
4584 }
4585
4586
4587 /**********************************************************************
4588  *         GetMenuContextHelpId    (USER32.@)
4589  */
4590 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4591 {
4592     LPPOPUPMENU menu;
4593
4594     TRACE("(%p)\n", hMenu);
4595
4596     if ((menu = MENU_GetMenu(hMenu)))
4597     {
4598         return menu->dwContextHelpID;
4599     }
4600     return 0;
4601 }
4602
4603 /**********************************************************************
4604  *         MenuItemFromPoint    (USER32.@)
4605  */
4606 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4607 {
4608     POPUPMENU *menu = MENU_GetMenu(hMenu);
4609     UINT pos;
4610
4611     /*FIXME: Do we have to handle hWnd here? */
4612     if (!menu) return -1;
4613     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4614     return pos;
4615 }
4616
4617
4618 /**********************************************************************
4619  *           translate_accelerator
4620  */
4621 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4622                                    BYTE fVirt, WORD key, WORD cmd )
4623 {
4624     INT mask = 0;
4625     UINT mesg = 0;
4626
4627     if (wParam != key) return FALSE;
4628
4629     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4630     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4631     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4632
4633     if (message == WM_CHAR || message == WM_SYSCHAR)
4634     {
4635         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
4636         {
4637             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4638             goto found;
4639         }
4640     }
4641     else
4642     {
4643         if(fVirt & FVIRTKEY)
4644         {
4645             TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4646                           wParam, 0xff & HIWORD(lParam));
4647
4648             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4649             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4650         }
4651         else
4652         {
4653             if (!(lParam & 0x01000000))  /* no special_key */
4654             {
4655                 if ((fVirt & FALT) && (lParam & 0x20000000))
4656                 {                              /* ^^ ALT pressed */
4657                     TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4658                     goto found;
4659                 }
4660             }
4661         }
4662     }
4663     return FALSE;
4664
4665  found:
4666     if (message == WM_KEYUP || message == WM_SYSKEYUP)
4667         mesg = 1;
4668     else
4669     {
4670         HMENU hMenu, hSubMenu, hSysMenu;
4671         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4672
4673         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4674         hSysMenu = get_win_sys_menu( hWnd );
4675
4676         /* find menu item and ask application to initialize it */
4677         /* 1. in the system menu */
4678         hSubMenu = hSysMenu;
4679         nPos = cmd;
4680         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4681         {
4682             if (GetCapture())
4683                 mesg = 2;
4684             if (!IsWindowEnabled(hWnd))
4685                 mesg = 3;
4686             else
4687             {
4688                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4689                 if(hSubMenu != hSysMenu)
4690                 {
4691                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4692                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4693                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4694                 }
4695                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4696             }
4697         }
4698         else /* 2. in the window's menu */
4699         {
4700             hSubMenu = hMenu;
4701             nPos = cmd;
4702             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4703             {
4704                 if (GetCapture())
4705                     mesg = 2;
4706                 if (!IsWindowEnabled(hWnd))
4707                     mesg = 3;
4708                 else
4709                 {
4710                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4711                     if(hSubMenu != hMenu)
4712                     {
4713                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4714                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4715                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4716                     }
4717                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4718                 }
4719             }
4720         }
4721
4722         if (mesg == 0)
4723         {
4724             if (uSysStat != (UINT)-1)
4725             {
4726                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4727                     mesg=4;
4728                 else
4729                     mesg=WM_SYSCOMMAND;
4730             }
4731             else
4732             {
4733                 if (uStat != (UINT)-1)
4734                 {
4735                     if (IsIconic(hWnd))
4736                         mesg=5;
4737                     else
4738                     {
4739                         if (uStat & (MF_DISABLED|MF_GRAYED))
4740                             mesg=6;
4741                         else
4742                             mesg=WM_COMMAND;
4743                     }
4744                 }
4745                 else
4746                     mesg=WM_COMMAND;
4747             }
4748         }
4749     }
4750
4751     if( mesg==WM_COMMAND )
4752     {
4753         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4754         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4755     }
4756     else if( mesg==WM_SYSCOMMAND )
4757     {
4758         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4759         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4760     }
4761     else
4762     {
4763         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
4764          *   #0: unknown (please report!)
4765          *   #1: for WM_KEYUP,WM_SYSKEYUP
4766          *   #2: mouse is captured
4767          *   #3: window is disabled
4768          *   #4: it's a disabled system menu option
4769          *   #5: it's a menu option, but window is iconic
4770          *   #6: it's a menu option, but disabled
4771          */
4772         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4773         if(mesg==0)
4774             ERR_(accel)(" unknown reason - please report!\n");
4775     }
4776     return TRUE;
4777 }
4778
4779 /**********************************************************************
4780  *      TranslateAccelerator      (USER32.@)
4781  *      TranslateAcceleratorA     (USER32.@)
4782  */
4783 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
4784 {
4785     /* YES, Accel16! */
4786     LPACCEL16 lpAccelTbl;
4787     int i;
4788     WPARAM wParam;
4789
4790     if (!hWnd || !msg) return 0;
4791
4792     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4793     {
4794         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4795         return 0;
4796     }
4797
4798     wParam = msg->wParam;
4799
4800     switch (msg->message)
4801     {
4802     case WM_KEYDOWN:
4803     case WM_SYSKEYDOWN:
4804         break;
4805
4806     case WM_CHAR:
4807     case WM_SYSCHAR:
4808         {
4809             char ch = LOWORD(wParam);
4810             WCHAR wch;
4811             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
4812             wParam = MAKEWPARAM(wch, HIWORD(wParam));
4813         }
4814         break;
4815
4816     default:
4817         return 0;
4818     }
4819
4820     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4821                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4822     i = 0;
4823     do
4824     {
4825         if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
4826                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4827             return 1;
4828     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4829
4830     return 0;
4831 }
4832
4833 /**********************************************************************
4834  *      TranslateAcceleratorW     (USER32.@)
4835  */
4836 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4837 {
4838     /* YES, Accel16! */
4839     LPACCEL16 lpAccelTbl;
4840     int i;
4841
4842     if (!hWnd || !msg) return 0;
4843
4844     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4845     {
4846         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4847         return 0;
4848     }
4849
4850     switch (msg->message)
4851     {
4852     case WM_KEYDOWN:
4853     case WM_SYSKEYDOWN:
4854     case WM_CHAR:
4855     case WM_SYSCHAR:
4856         break;
4857
4858     default:
4859         return 0;
4860     }
4861
4862     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
4863                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4864     i = 0;
4865     do
4866     {
4867         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4868                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4869             return 1;
4870     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4871
4872     return 0;
4873 }