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