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