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