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