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