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