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