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