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