Change RECT to use LONG to match win32 standard headers and fix format
[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=%p", mp->hSubMenu);
223         if (flags) {
224             int count = 0;
225             DPRINTF( ", Type=");
226             if (type == MFT_STRING)
227                 /* Nothing */ ;
228             else if (type == MFT_SEPARATOR)
229                 MENUOUT("sep");
230             else if (type == MFT_OWNERDRAW)
231                 MENUOUT("own");
232             else if (type == MFT_BITMAP)
233                 MENUOUT("bit");
234             else
235                 MENUOUT("???");
236             flags -= type;
237
238             MENUFLAG(MF_POPUP, "pop");
239             MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240             MENUFLAG(MFT_MENUBREAK, "brk");
241             MENUFLAG(MFT_RADIOCHECK, "radio");
242             MENUFLAG(MFT_RIGHTORDER, "rorder");
243             MENUFLAG(MF_SYSMENU, "sys");
244             MENUFLAG(MFT_RIGHTJUSTIFY, "right");        /* same as MF_HELP */
245
246             if (flags)
247                 DPRINTF( "+0x%x", flags);
248         }
249         flags = mp->fState;
250         if (flags) {
251             int count = 0;
252             DPRINTF( ", State=");
253             MENUFLAG(MFS_GRAYED, "grey");
254             MENUFLAG(MFS_DEFAULT, "default");
255             MENUFLAG(MFS_DISABLED, "dis");
256             MENUFLAG(MFS_CHECKED, "check");
257             MENUFLAG(MFS_HILITE, "hi");
258             MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259             MENUFLAG(MF_MOUSESELECT, "mouse");
260             if (flags)
261                 DPRINTF( "+0x%x", flags);
262         }
263         if (mp->hCheckBit)
264             DPRINTF( ", Chk=%p", mp->hCheckBit);
265         if (mp->hUnCheckBit)
266             DPRINTF( ", Unc=%p", mp->hUnCheckBit);
267
268         if (type == MFT_STRING) {
269             if (mp->text)
270                 DPRINTF( ", Text=%s", debugstr_w(mp->text));
271             else
272                 DPRINTF( ", Text=Null");
273         } else if (mp->text == NULL)
274             /* Nothing */ ;
275         else
276             DPRINTF( ", Text=%p", mp->text);
277         if (mp->dwItemData)
278             DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
279         DPRINTF( " }");
280     } else {
281         DPRINTF( "NULL");
282     }
283
284     DPRINTF(" %s\n", postfix);
285 }
286
287 #undef MENUOUT
288 #undef MENUFLAG
289
290
291 /***********************************************************************
292  *           MENU_GetMenu
293  *
294  * Validate the given menu handle and returns the menu structure pointer.
295  */
296 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
297 {
298     POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
299     if (!menu || menu->wMagic != MENU_MAGIC)
300     {
301         WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302         menu = NULL;
303     }
304     return menu;
305 }
306
307 /***********************************************************************
308  *           get_win_sys_menu
309  *
310  * Get the system menu of a window
311  */
312 static HMENU get_win_sys_menu( HWND hwnd )
313 {
314     HMENU ret = 0;
315     WND *win = WIN_FindWndPtr( hwnd );
316     if (win)
317     {
318         ret = win->hSysMenu;
319         WIN_ReleaseWndPtr( win );
320     }
321     return ret;
322 }
323
324 /***********************************************************************
325  *           MENU_CopySysPopup
326  *
327  * Return the default system menu.
328  */
329 static HMENU MENU_CopySysPopup(void)
330 {
331     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 %p.\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 %p, hPopupMenu %p\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 %p (hMenu %p)\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=%p (hPopup %p)\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 == (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 [%p]\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 (INT_PTR)HBMMENU_SYSTEM:
723             if (data)
724             {
725                 bmp = (HBITMAP)data;
726                 break;
727             }
728             /* fall through */
729         case (INT_PTR)HBMMENU_MBAR_RESTORE:
730         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
731         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
732         case (INT_PTR)HBMMENU_MBAR_CLOSE:
733         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
734             size->cx = GetSystemMetrics( SM_CXSIZE );
735             size->cy = GetSystemMetrics( SM_CYSIZE );
736             return;
737         case (INT_PTR)HBMMENU_CALLBACK:
738         case (INT_PTR)HBMMENU_POPUP_CLOSE:
739         case (INT_PTR)HBMMENU_POPUP_RESTORE:
740         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
741         case (INT_PTR)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 (INT_PTR)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 (INT_PTR)HBMMENU_MBAR_RESTORE:
794             flags = DFCS_CAPTIONRESTORE;
795             break;
796         case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
797             flags = DFCS_CAPTIONMIN;
798             break;
799         case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
800             flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
801             break;
802         case (INT_PTR)HBMMENU_MBAR_CLOSE:
803             flags = DFCS_CAPTIONCLOSE;
804             break;
805         case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
806             flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
807             break;
808         case (INT_PTR)HBMMENU_CALLBACK:
809         case (INT_PTR)HBMMENU_POPUP_CLOSE:
810         case (INT_PTR)HBMMENU_POPUP_RESTORE:
811         case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
812         case (INT_PTR)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=%p owner=%p (%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("(%ld,%ld)-(%ld,%ld)\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=%ld top=%ld right=%ld bottom=%ld\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|ODS_DISABLED;
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=%p itemID=%d, itemState=%d, itemAction=%d, "
1172               "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\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={%ld,%ld,%ld,%ld}\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=%p dc=%p menu=%p\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("(%p, %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=%p hmenu=%p 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=%p menu=%p 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=%p hmenu=%p 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_32(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=%p hmenu=%p 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=%p hmenu=%p 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=%p\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("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2202
2203     if (!(item->fType & MF_POPUP))
2204     {
2205         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2206         {
2207             /* If TPM_RETURNCMD is set you return the id, but
2208                do not send a message to the owner */
2209             if(!(wFlags & TPM_RETURNCMD))
2210             {
2211                 if( menu->wFlags & MF_SYSMENU )
2212                     PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2213                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2214                 else
2215                     PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2216             }
2217             return item->wID;
2218         }
2219     }
2220     else
2221         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2222
2223     return -1;
2224 }
2225
2226 /***********************************************************************
2227  *           MENU_SwitchTracking
2228  *
2229  * Helper function for menu navigation routines.
2230  */
2231 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2232 {
2233     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2234     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2235
2236     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2237
2238     if( pmt->hTopMenu != hPtMenu &&
2239         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2240     {
2241         /* both are top level menus (system and menu-bar) */
2242         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2243         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2244         pmt->hTopMenu = hPtMenu;
2245     }
2246     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2247     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2248 }
2249
2250
2251 /***********************************************************************
2252  *           MENU_ButtonDown
2253  *
2254  * Return TRUE if we can go on with menu tracking.
2255  */
2256 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2257 {
2258     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2259
2260     if (hPtMenu)
2261     {
2262         UINT id = 0;
2263         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2264         MENUITEM *item;
2265
2266         if( IS_SYSTEM_MENU(ptmenu) )
2267             item = ptmenu->items;
2268         else
2269             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2270
2271         if( item )
2272         {
2273             if( ptmenu->FocusedItem != id )
2274                 MENU_SwitchTracking( pmt, hPtMenu, id );
2275
2276             /* If the popup menu is not already "popped" */
2277             if(!(item->fState & MF_MOUSESELECT ))
2278             {
2279                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2280
2281                 /* In win31, a newly popped menu always remains opened for the next buttonup */
2282                 if(TWEAK_WineLook == WIN31_LOOK)
2283                     ptmenu->bTimeToHide = FALSE;
2284             }
2285
2286             return TRUE;
2287         }
2288         /* Else the click was on the menu bar, finish the tracking */
2289     }
2290     return FALSE;
2291 }
2292
2293 /***********************************************************************
2294  *           MENU_ButtonUp
2295  *
2296  * Return the value of MENU_ExecFocusedItem if
2297  * the selected item was not a popup. Else open the popup.
2298  * A -1 return value indicates that we go on with menu tracking.
2299  *
2300  */
2301 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2302 {
2303     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2304
2305     if (hPtMenu)
2306     {
2307         UINT id = 0;
2308         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2309         MENUITEM *item;
2310
2311         if( IS_SYSTEM_MENU(ptmenu) )
2312             item = ptmenu->items;
2313         else
2314             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2315
2316         if( item && (ptmenu->FocusedItem == id ))
2317         {
2318             if( !(item->fType & MF_POPUP) )
2319                 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2320
2321             /* If we are dealing with the top-level menu            */
2322             /* and this is a click on an already "popped" item:     */
2323             /* Stop the menu tracking and close the opened submenus */
2324             if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2325                 return 0;
2326         }
2327         ptmenu->bTimeToHide = TRUE;
2328     }
2329     return -1;
2330 }
2331
2332
2333 /***********************************************************************
2334  *           MENU_MouseMove
2335  *
2336  * Return TRUE if we can go on with menu tracking.
2337  */
2338 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2339 {
2340     UINT id = NO_SELECTED_ITEM;
2341     POPUPMENU *ptmenu = NULL;
2342
2343     if( hPtMenu )
2344     {
2345         ptmenu = MENU_GetMenu( hPtMenu );
2346         if( IS_SYSTEM_MENU(ptmenu) )
2347             id = 0;
2348         else
2349             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2350     }
2351
2352     if( id == NO_SELECTED_ITEM )
2353     {
2354         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2355                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2356
2357     }
2358     else if( ptmenu->FocusedItem != id )
2359     {
2360             MENU_SwitchTracking( pmt, hPtMenu, id );
2361             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2362     }
2363     return TRUE;
2364 }
2365
2366
2367 /***********************************************************************
2368  *           MENU_SetCapture
2369  */
2370 static void MENU_SetCapture( HWND hwnd )
2371 {
2372     HWND previous = 0;
2373
2374     SERVER_START_REQ( set_capture_window )
2375     {
2376         req->handle = hwnd;
2377         req->flags  = CAPTURE_MENU;
2378         if (!wine_server_call_err( req ))
2379         {
2380             previous = reply->previous;
2381             hwnd = reply->full_handle;
2382         }
2383     }
2384     SERVER_END_REQ;
2385
2386     if (previous && previous != hwnd)
2387         SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2388 }
2389
2390
2391 /***********************************************************************
2392  *           MENU_DoNextMenu
2393  *
2394  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2395  */
2396 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2397 {
2398     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2399
2400     if( (vk == VK_LEFT &&  menu->FocusedItem == 0 ) ||
2401         (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2402     {
2403         MDINEXTMENU next_menu;
2404         HMENU hNewMenu;
2405         HWND  hNewWnd;
2406         UINT  id = 0;
2407
2408         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2409         next_menu.hmenuNext = 0;
2410         next_menu.hwndNext = 0;
2411         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2412
2413         TRACE("%p [%p] -> %p [%p]\n",
2414               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2415
2416         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2417         {
2418             DWORD style = GetWindowLongA( pmt->hOwnerWnd, GWL_STYLE );
2419             hNewWnd = pmt->hOwnerWnd;
2420             if( IS_SYSTEM_MENU(menu) )
2421             {
2422                 /* switch to the menu bar */
2423
2424                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2425
2426                 if( vk == VK_LEFT )
2427                 {
2428                     menu = MENU_GetMenu( hNewMenu );
2429                     id = menu->nItems - 1;
2430                 }
2431             }
2432             else if (style & WS_SYSMENU )
2433             {
2434                 /* switch to the system menu */
2435                 hNewMenu = get_win_sys_menu( hNewWnd );
2436             }
2437             else return FALSE;
2438         }
2439         else    /* application returned a new menu to switch to */
2440         {
2441             hNewMenu = next_menu.hmenuNext;
2442             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2443
2444             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2445             {
2446                 DWORD style = GetWindowLongA( hNewWnd, GWL_STYLE );
2447
2448                 if (style & WS_SYSMENU &&
2449                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2450                 {
2451                     /* get the real system menu */
2452                     hNewMenu =  get_win_sys_menu(hNewWnd);
2453                 }
2454                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2455                 {
2456                     /* FIXME: Not sure what to do here;
2457                      * perhaps try to track hNewMenu as a popup? */
2458
2459                     TRACE(" -- got confused.\n");
2460                     return FALSE;
2461                 }
2462             }
2463             else return FALSE;
2464         }
2465
2466         if( hNewMenu != pmt->hTopMenu )
2467         {
2468             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2469                     FALSE, 0 );
2470             if( pmt->hCurrentMenu != pmt->hTopMenu )
2471                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2472         }
2473
2474         if( hNewWnd != pmt->hOwnerWnd )
2475         {
2476             pmt->hOwnerWnd = hNewWnd;
2477             MENU_SetCapture( pmt->hOwnerWnd );
2478         }
2479
2480         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2481         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2482
2483         return TRUE;
2484     }
2485     return FALSE;
2486 }
2487
2488 /***********************************************************************
2489  *           MENU_SuspendPopup
2490  *
2491  * The idea is not to show the popup if the next input message is
2492  * going to hide it anyway.
2493  */
2494 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2495 {
2496     MSG msg;
2497
2498     msg.hwnd = pmt->hOwnerWnd;
2499
2500     PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2501     pmt->trackFlags |= TF_SKIPREMOVE;
2502
2503     switch( uMsg )
2504     {
2505         case WM_KEYDOWN:
2506              PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2507              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2508              {
2509                  PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2510                  PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2511                  if( msg.message == WM_KEYDOWN &&
2512                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2513                  {
2514                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2515                      return TRUE;
2516                  }
2517              }
2518              break;
2519     }
2520
2521     /* failures go through this */
2522     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2523     return FALSE;
2524 }
2525
2526 /***********************************************************************
2527  *           MENU_KeyEscape
2528  *
2529  * Handle a VK_ESCAPE key event in a menu.
2530  */
2531 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2532 {
2533     BOOL bEndMenu = TRUE;
2534
2535     if (pmt->hCurrentMenu != pmt->hTopMenu)
2536     {
2537         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2538
2539         if (menu->wFlags & MF_POPUP)
2540         {
2541             HMENU hmenutmp, hmenuprev;
2542
2543             hmenuprev = hmenutmp = pmt->hTopMenu;
2544
2545             /* close topmost popup */
2546             while (hmenutmp != pmt->hCurrentMenu)
2547             {
2548                 hmenuprev = hmenutmp;
2549                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2550             }
2551
2552             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2553             pmt->hCurrentMenu = hmenuprev;
2554             bEndMenu = FALSE;
2555         }
2556     }
2557
2558     return bEndMenu;
2559 }
2560
2561 /***********************************************************************
2562  *           MENU_KeyLeft
2563  *
2564  * Handle a VK_LEFT key event in a menu.
2565  */
2566 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2567 {
2568     POPUPMENU *menu;
2569     HMENU hmenutmp, hmenuprev;
2570     UINT  prevcol;
2571
2572     hmenuprev = hmenutmp = pmt->hTopMenu;
2573     menu = MENU_GetMenu( hmenutmp );
2574
2575     /* Try to move 1 column left (if possible) */
2576     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2577         NO_SELECTED_ITEM ) {
2578
2579         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2580                          prevcol, TRUE, 0 );
2581         return;
2582     }
2583
2584     /* close topmost popup */
2585     while (hmenutmp != pmt->hCurrentMenu)
2586     {
2587         hmenuprev = hmenutmp;
2588         hmenutmp = MENU_GetSubPopup( hmenuprev );
2589     }
2590
2591     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2592     pmt->hCurrentMenu = hmenuprev;
2593
2594     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2595     {
2596         /* move menu bar selection if no more popups are left */
2597
2598         if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2599              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2600
2601         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2602         {
2603            /* A sublevel menu was displayed - display the next one
2604             * unless there is another displacement coming up */
2605
2606             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2607                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2608                                                 pmt->hTopMenu, TRUE, wFlags);
2609         }
2610     }
2611 }
2612
2613
2614 /***********************************************************************
2615  *           MENU_KeyRight
2616  *
2617  * Handle a VK_RIGHT key event in a menu.
2618  */
2619 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2620 {
2621     HMENU hmenutmp;
2622     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2623     UINT  nextcol;
2624
2625     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2626           pmt->hCurrentMenu,
2627           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2628           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2629
2630     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2631     {
2632         /* If already displaying a popup, try to display sub-popup */
2633
2634         hmenutmp = pmt->hCurrentMenu;
2635         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2636
2637         /* if subpopup was displayed then we are done */
2638         if (hmenutmp != pmt->hCurrentMenu) return;
2639     }
2640
2641     /* Check to see if there's another column */
2642     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2643         NO_SELECTED_ITEM ) {
2644         TRACE("Going to %d.\n", nextcol );
2645         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2646                          nextcol, TRUE, 0 );
2647         return;
2648     }
2649
2650     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2651     {
2652         if( pmt->hCurrentMenu != pmt->hTopMenu )
2653         {
2654             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2655             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2656         } else hmenutmp = 0;
2657
2658         /* try to move to the next item */
2659         if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2660              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2661
2662         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2663             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2664                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2665                                                        pmt->hTopMenu, TRUE, wFlags);
2666     }
2667 }
2668
2669 /***********************************************************************
2670  *           MENU_TrackMenu
2671  *
2672  * Menu tracking code.
2673  */
2674 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2675                               HWND hwnd, const RECT *lprect )
2676 {
2677     MSG msg;
2678     POPUPMENU *menu;
2679     BOOL fRemove;
2680     INT executedMenuId = -1;
2681     MTRACKER mt;
2682     BOOL enterIdleSent = FALSE;
2683
2684     mt.trackFlags = 0;
2685     mt.hCurrentMenu = hmenu;
2686     mt.hTopMenu = hmenu;
2687     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2688     mt.pt.x = x;
2689     mt.pt.y = y;
2690
2691     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2692           hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2693           (lprect) ? lprect->right : 0,  (lprect) ? lprect->bottom : 0);
2694
2695     fEndMenu = FALSE;
2696     if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2697
2698     if (wFlags & TPM_BUTTONDOWN)
2699     {
2700         /* Get the result in order to start the tracking or not */
2701         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2702         fEndMenu = !fRemove;
2703     }
2704
2705     MENU_SetCapture( mt.hOwnerWnd );
2706
2707     while (!fEndMenu)
2708     {
2709         menu = MENU_GetMenu( mt.hCurrentMenu );
2710         if (!menu) /* sometimes happens if I do a window manager close */
2711             break;
2712
2713         /* we have to keep the message in the queue until it's
2714          * clear that menu loop is not over yet. */
2715
2716         for (;;)
2717         {
2718             if (PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ))
2719             {
2720                 if (!CallMsgFilterA( &msg, MSGF_MENU )) break;
2721                 /* remove the message from the queue */
2722                 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2723             }
2724             else
2725             {
2726                 if (!enterIdleSent)
2727                 {
2728                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2729                     enterIdleSent = TRUE;
2730                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2731                 }
2732                 WaitMessage();
2733             }
2734         }
2735
2736         /* check if EndMenu() tried to cancel us, by posting this message */
2737         if(msg.message == WM_CANCELMODE)
2738         {
2739             /* we are now out of the loop */
2740             fEndMenu = TRUE;
2741
2742             /* remove the message from the queue */
2743             PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2744
2745             /* break out of internal loop, ala ESCAPE */
2746             break;
2747         }
2748
2749         TranslateMessage( &msg );
2750         mt.pt = msg.pt;
2751
2752         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2753           enterIdleSent=FALSE;
2754
2755         fRemove = FALSE;
2756         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2757         {
2758             /*
2759              * Use the mouse coordinates in lParam instead of those in the MSG
2760              * struct to properly handle synthetic messages. They are already
2761              * in screen coordinates.
2762              */
2763             mt.pt.x = SLOWORD(msg.lParam);
2764             mt.pt.y = SHIWORD(msg.lParam);
2765
2766             /* Find a menu for this mouse event */
2767             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2768
2769             switch(msg.message)
2770             {
2771                 /* no WM_NC... messages in captured state */
2772
2773                 case WM_RBUTTONDBLCLK:
2774                 case WM_RBUTTONDOWN:
2775                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2776                     /* fall through */
2777                 case WM_LBUTTONDBLCLK:
2778                 case WM_LBUTTONDOWN:
2779                     /* If the message belongs to the menu, removes it from the queue */
2780                     /* Else, end menu tracking */
2781                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2782                     fEndMenu = !fRemove;
2783                     break;
2784
2785                 case WM_RBUTTONUP:
2786                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2787                     /* fall through */
2788                 case WM_LBUTTONUP:
2789                     /* Check if a menu was selected by the mouse */
2790                     if (hmenu)
2791                     {
2792                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2793
2794                         /* End the loop if executedMenuId is an item ID */
2795                         /* or if the job was done (executedMenuId = 0). */
2796                         fEndMenu = fRemove = (executedMenuId != -1);
2797                     }
2798                     /* No menu was selected by the mouse */
2799                     /* if the function was called by TrackPopupMenu, continue
2800                        with the menu tracking. If not, stop it */
2801                     else
2802                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2803
2804                     break;
2805
2806                 case WM_MOUSEMOVE:
2807                     /* In win95 winelook, the selected menu item must be changed every time the
2808                        mouse moves. In Win31 winelook, the mouse button has to be held down */
2809
2810                     if ( hmenu && ((TWEAK_WineLook > WIN31_LOOK) ||
2811                          ( (msg.wParam & MK_LBUTTON) ||
2812                            ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))) )
2813
2814                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2815
2816             } /* switch(msg.message) - mouse */
2817         }
2818         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2819         {
2820             fRemove = TRUE;  /* Keyboard messages are always removed */
2821             switch(msg.message)
2822             {
2823             case WM_KEYDOWN:
2824                 switch(msg.wParam)
2825                 {
2826                 case VK_HOME:
2827                 case VK_END:
2828                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2829                                      NO_SELECTED_ITEM, FALSE, 0 );
2830                 /* fall through */
2831                 case VK_UP:
2832                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2833                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2834                     break;
2835
2836                 case VK_DOWN: /* If on menu bar, pull-down the menu */
2837
2838                     menu = MENU_GetMenu( mt.hCurrentMenu );
2839                     if (!(menu->wFlags & MF_POPUP))
2840                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2841                     else      /* otherwise try to move selection */
2842                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2843                     break;
2844
2845                 case VK_LEFT:
2846                     MENU_KeyLeft( &mt, wFlags );
2847                     break;
2848
2849                 case VK_RIGHT:
2850                     MENU_KeyRight( &mt, wFlags );
2851                     break;
2852
2853                 case VK_ESCAPE:
2854                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
2855                     break;
2856
2857                 case VK_F1:
2858                     {
2859                         HELPINFO hi;
2860                         hi.cbSize = sizeof(HELPINFO);
2861                         hi.iContextType = HELPINFO_MENUITEM;
2862                         if (menu->FocusedItem == NO_SELECTED_ITEM)
2863                             hi.iCtrlId = 0;
2864                         else
2865                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2866                         hi.hItemHandle = hmenu;
2867                         hi.dwContextId = menu->dwContextHelpID;
2868                         hi.MousePos = msg.pt;
2869                         SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2870                         break;
2871                     }
2872
2873                 default:
2874                     break;
2875                 }
2876                 break;  /* WM_KEYDOWN */
2877
2878             case WM_SYSKEYDOWN:
2879                 switch(msg.wParam)
2880                 {
2881                 case VK_MENU:
2882                     fEndMenu = TRUE;
2883                     break;
2884
2885                 }
2886                 break;  /* WM_SYSKEYDOWN */
2887
2888             case WM_CHAR:
2889                 {
2890                     UINT        pos;
2891
2892                     if (msg.wParam == '\r' || msg.wParam == ' ')
2893                     {
2894                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2895                         fEndMenu = (executedMenuId != -1);
2896
2897                         break;
2898                     }
2899
2900                       /* Hack to avoid control chars. */
2901                       /* We will find a better way real soon... */
2902                     if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2903
2904                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2905                                               LOWORD(msg.wParam), FALSE );
2906                     if (pos == (UINT)-2) fEndMenu = TRUE;
2907                     else if (pos == (UINT)-1) MessageBeep(0);
2908                     else
2909                     {
2910                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2911                                 TRUE, 0 );
2912                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2913                         fEndMenu = (executedMenuId != -1);
2914                     }
2915                 }
2916                 break;
2917             }  /* switch(msg.message) - kbd */
2918         }
2919         else
2920         {
2921             DispatchMessageA( &msg );
2922         }
2923
2924         if (!fEndMenu) fRemove = TRUE;
2925
2926         /* finally remove message from the queue */
2927
2928         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2929             PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2930         else mt.trackFlags &= ~TF_SKIPREMOVE;
2931     }
2932
2933     MENU_SetCapture(0);  /* release the capture */
2934
2935     /* If dropdown is still painted and the close box is clicked on
2936        then the menu will be destroyed as part of the DispatchMessage above.
2937        This will then invalidate the menu handle in mt.hTopMenu. We should
2938        check for this first.  */
2939     if( IsMenu( mt.hTopMenu ) )
2940     {
2941         menu = MENU_GetMenu( mt.hTopMenu );
2942
2943         if( IsWindow( mt.hOwnerWnd ) )
2944         {
2945             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2946
2947             if (menu && menu->wFlags & MF_POPUP)
2948             {
2949                 DestroyWindow( menu->hWnd );
2950                 menu->hWnd = 0;
2951             }
2952             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2953             SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2954         }
2955
2956         /* Reset the variable for hiding menu */
2957         if( menu ) menu->bTimeToHide = FALSE;
2958     }
2959
2960     /* The return value is only used by TrackPopupMenu */
2961     return ((executedMenuId != -1) ? executedMenuId : 0);
2962 }
2963
2964 /***********************************************************************
2965  *           MENU_InitTracking
2966  */
2967 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2968 {
2969     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2970
2971     HideCaret(0);
2972
2973     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2974     if (!(wFlags & TPM_NONOTIFY))
2975        SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2976
2977     SendMessageA( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2978
2979     if (!(wFlags & TPM_NONOTIFY))
2980     {
2981        POPUPMENU *menu;
2982        SendMessageA( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2983        if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2984        { /* app changed/recreated menu bar entries in WM_INITMENU
2985             Recalculate menu sizes else clicks will not work */
2986           SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2987                         SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2988
2989        }
2990     }
2991     return TRUE;
2992 }
2993 /***********************************************************************
2994  *           MENU_ExitTracking
2995  */
2996 static BOOL MENU_ExitTracking(HWND hWnd)
2997 {
2998     TRACE("hwnd=%p\n", hWnd);
2999
3000     SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3001     ShowCaret(0);
3002     return TRUE;
3003 }
3004
3005 /***********************************************************************
3006  *           MENU_TrackMouseMenuBar
3007  *
3008  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3009  */
3010 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3011 {
3012     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3013     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3014
3015     TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3016
3017     if (IsMenu(hMenu))
3018     {
3019         /* map point to parent client coordinates */
3020         HWND parent = GetAncestor( hWnd, GA_PARENT );
3021         if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
3022
3023         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3024         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3025         MENU_ExitTracking(hWnd);
3026     }
3027 }
3028
3029
3030 /***********************************************************************
3031  *           MENU_TrackKbdMenuBar
3032  *
3033  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3034  */
3035 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, INT vkey)
3036 {
3037     UINT uItem = NO_SELECTED_ITEM;
3038     HMENU hTrackMenu;
3039     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3040
3041     /* find window that has a menu */
3042
3043     while (GetWindowLongA( hwnd, GWL_STYLE ) & WS_CHILD)
3044         if (!(hwnd = GetParent( hwnd ))) return;
3045
3046     /* check if we have to track a system menu */
3047
3048     hTrackMenu = GetMenu( hwnd );
3049     if (!hTrackMenu || IsIconic(hwnd) || vkey == VK_SPACE )
3050     {
3051         if (!(GetWindowLongA( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3052         hTrackMenu = get_win_sys_menu( hwnd );
3053         uItem = 0;
3054         wParam |= HTSYSMENU; /* prevent item lookup */
3055     }
3056
3057     if (!IsMenu( hTrackMenu )) return;
3058
3059     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3060
3061     if( vkey && vkey != VK_SPACE )
3062     {
3063         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, vkey, (wParam & HTSYSMENU) );
3064         if( uItem >= (UINT)(-2) )
3065         {
3066             if( uItem == (UINT)(-1) ) MessageBeep(0);
3067             hTrackMenu = 0;
3068         }
3069     }
3070
3071     if( hTrackMenu )
3072     {
3073         MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3074
3075         if( uItem == NO_SELECTED_ITEM )
3076             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3077         else if( vkey )
3078             PostMessageA( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3079
3080         MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3081     }
3082     MENU_ExitTracking( hwnd );
3083 }
3084
3085
3086 /**********************************************************************
3087  *           TrackPopupMenu   (USER32.@)
3088  *
3089  * Like the win32 API, the function return the command ID only if the
3090  * flag TPM_RETURNCMD is on.
3091  *
3092  */
3093 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3094                            INT nReserved, HWND hWnd, const RECT *lpRect )
3095 {
3096     BOOL ret = FALSE;
3097
3098     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3099
3100     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3101     if (!(wFlags & TPM_NONOTIFY))
3102         SendMessageA( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3103
3104     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3105         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3106     MENU_ExitTracking(hWnd);
3107
3108     if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3109         ret = 1;
3110
3111     return ret;
3112 }
3113
3114 /**********************************************************************
3115  *           TrackPopupMenuEx   (USER32.@)
3116  */
3117 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3118                                 HWND hWnd, LPTPMPARAMS lpTpm )
3119 {
3120     FIXME("not fully implemented\n" );
3121     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3122                              lpTpm ? &lpTpm->rcExclude : NULL );
3123 }
3124
3125 /***********************************************************************
3126  *           PopupMenuWndProc
3127  *
3128  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3129  */
3130 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3131 {
3132     TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3133
3134     switch(message)
3135     {
3136     case WM_CREATE:
3137         {
3138             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3139             SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3140             return 0;
3141         }
3142
3143     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3144         return MA_NOACTIVATE;
3145
3146     case WM_PAINT:
3147         {
3148             PAINTSTRUCT ps;
3149             BeginPaint( hwnd, &ps );
3150             MENU_DrawPopupMenu( hwnd, ps.hdc,
3151                                 (HMENU)GetWindowLongA( hwnd, 0 ) );
3152             EndPaint( hwnd, &ps );
3153             return 0;
3154         }
3155     case WM_ERASEBKGND:
3156         return 1;
3157
3158     case WM_DESTROY:
3159         /* zero out global pointer in case resident popup window was destroyed. */
3160         if (hwnd == top_popup) top_popup = 0;
3161         break;
3162
3163     case WM_SHOWWINDOW:
3164
3165         if( wParam )
3166         {
3167             if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3168         }
3169         else
3170             SetWindowLongW( hwnd, 0, 0 );
3171         break;
3172
3173     case MM_SETMENUHANDLE:
3174         SetWindowLongW( hwnd, 0, wParam );
3175         break;
3176
3177     case MM_GETMENUHANDLE:
3178         return GetWindowLongW( hwnd, 0 );
3179
3180     default:
3181         return DefWindowProcW( hwnd, message, wParam, lParam );
3182     }
3183     return 0;
3184 }
3185
3186
3187 /***********************************************************************
3188  *           MENU_GetMenuBarHeight
3189  *
3190  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3191  */
3192 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3193                               INT orgX, INT orgY )
3194 {
3195     HDC hdc;
3196     RECT rectBar;
3197     LPPOPUPMENU lppop;
3198
3199     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3200
3201     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3202
3203     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3204     SelectObject( hdc, hMenuFont);
3205     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3206     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3207     ReleaseDC( hwnd, hdc );
3208     return lppop->Height;
3209 }
3210
3211
3212 /*******************************************************************
3213  *         ChangeMenuA    (USER32.@)
3214  */
3215 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3216                              UINT id, UINT flags )
3217 {
3218     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3219     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3220                                                  id, data );
3221     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3222     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3223                                                 id, data );
3224     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3225                                               flags & MF_BYPOSITION ? pos : id,
3226                                               flags & ~MF_REMOVE );
3227     /* Default: MF_INSERT */
3228     return InsertMenuA( hMenu, pos, flags, id, data );
3229 }
3230
3231
3232 /*******************************************************************
3233  *         ChangeMenuW    (USER32.@)
3234  */
3235 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3236                              UINT id, UINT flags )
3237 {
3238     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3239     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3240                                                  id, data );
3241     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3242     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3243                                                 id, data );
3244     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3245                                               flags & MF_BYPOSITION ? pos : id,
3246                                               flags & ~MF_REMOVE );
3247     /* Default: MF_INSERT */
3248     return InsertMenuW( hMenu, pos, flags, id, data );
3249 }
3250
3251
3252 /*******************************************************************
3253  *         CheckMenuItem    (USER32.@)
3254  */
3255 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3256 {
3257     MENUITEM *item;
3258     DWORD ret;
3259
3260     TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3261     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3262     ret = item->fState & MF_CHECKED;
3263     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3264     else item->fState &= ~MF_CHECKED;
3265     return ret;
3266 }
3267
3268
3269 /**********************************************************************
3270  *         EnableMenuItem    (USER32.@)
3271  */
3272 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3273 {
3274     UINT    oldflags;
3275     MENUITEM *item;
3276     POPUPMENU *menu;
3277
3278     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3279
3280     /* Get the Popupmenu to access the owner menu */
3281     if (!(menu = MENU_GetMenu(hMenu)))
3282         return (UINT)-1;
3283
3284     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3285         return (UINT)-1;
3286
3287     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3288     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3289
3290     /* In win95 if the close item in the system menu change update the close button */
3291     if (TWEAK_WineLook == WIN95_LOOK)
3292         if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3293         {
3294             if (menu->hSysMenuOwner != 0)
3295             {
3296                 POPUPMENU* parentMenu;
3297
3298                 /* Get the parent menu to access*/
3299                 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3300                     return (UINT)-1;
3301
3302                 /* Refresh the frame to reflect the change*/
3303                 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3304                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3305             }
3306         }
3307
3308     return oldflags;
3309 }
3310
3311
3312 /*******************************************************************
3313  *         GetMenuStringA    (USER32.@)
3314  */
3315 INT WINAPI GetMenuStringA(
3316         HMENU hMenu,    /* [in] menuhandle */
3317         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3318         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3319         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3320         UINT wFlags     /* [in] MF_ flags */
3321 ) {
3322     MENUITEM *item;
3323
3324     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3325     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3326     if (!IS_STRING_ITEM(item->fType)) return 0;
3327     if (!str || !nMaxSiz) return strlenW(item->text);
3328     str[0] = '\0';
3329     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3330         str[nMaxSiz-1] = 0;
3331     TRACE("returning '%s'\n", str );
3332     return strlen(str);
3333 }
3334
3335
3336 /*******************************************************************
3337  *         GetMenuStringW    (USER32.@)
3338  */
3339 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3340                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3341 {
3342     MENUITEM *item;
3343
3344     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3345     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3346     if (!IS_STRING_ITEM(item->fType)) return 0;
3347     if (!str || !nMaxSiz) return strlenW(item->text);
3348     str[0] = '\0';
3349     lstrcpynW( str, item->text, nMaxSiz );
3350     return strlenW(str);
3351 }
3352
3353
3354 /**********************************************************************
3355  *         HiliteMenuItem    (USER32.@)
3356  */
3357 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3358                                 UINT wHilite )
3359 {
3360     LPPOPUPMENU menu;
3361     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3362     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3363     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3364     if (menu->FocusedItem == wItemID) return TRUE;
3365     MENU_HideSubPopups( hWnd, hMenu, FALSE );
3366     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3367     return TRUE;
3368 }
3369
3370
3371 /**********************************************************************
3372  *         GetMenuState    (USER32.@)
3373  */
3374 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3375 {
3376     MENUITEM *item;
3377     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3378     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3379     debug_print_menuitem ("  item: ", item, "");
3380     if (item->fType & MF_POPUP)
3381     {
3382         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3383         if (!menu) return -1;
3384         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3385     }
3386     else
3387     {
3388         /* We used to (from way back then) mask the result to 0xff.  */
3389         /* I don't know why and it seems wrong as the documented */
3390         /* return flag MF_SEPARATOR is outside that mask.  */
3391         return (item->fType | item->fState);
3392     }
3393 }
3394
3395
3396 /**********************************************************************
3397  *         GetMenuItemCount    (USER32.@)
3398  */
3399 INT WINAPI GetMenuItemCount( HMENU hMenu )
3400 {
3401     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3402     if (!menu) return -1;
3403     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3404     return menu->nItems;
3405 }
3406
3407
3408 /**********************************************************************
3409  *         GetMenuItemID    (USER32.@)
3410  */
3411 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3412 {
3413     MENUITEM * lpmi;
3414
3415     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3416     if (lpmi->fType & MF_POPUP) return -1;
3417     return lpmi->wID;
3418
3419 }
3420
3421
3422 /*******************************************************************
3423  *         InsertMenuW    (USER32.@)
3424  */
3425 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3426                          UINT_PTR id, LPCWSTR str )
3427 {
3428     MENUITEM *item;
3429
3430     if (IS_STRING_ITEM(flags) && str)
3431         TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3432               hMenu, pos, flags, id, debugstr_w(str) );
3433     else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3434                hMenu, pos, flags, id, (DWORD)str );
3435
3436     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3437
3438     if (!(MENU_SetItemData( item, flags, id, str )))
3439     {
3440         RemoveMenu( hMenu, pos, flags );
3441         return FALSE;
3442     }
3443
3444     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3445         (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3446
3447     item->hCheckBit = item->hUnCheckBit = 0;
3448     return TRUE;
3449 }
3450
3451
3452 /*******************************************************************
3453  *         InsertMenuA    (USER32.@)
3454  */
3455 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3456                          UINT_PTR id, LPCSTR str )
3457 {
3458     BOOL ret = FALSE;
3459
3460     if (IS_STRING_ITEM(flags) && str)
3461     {
3462         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3463         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3464         if (newstr)
3465         {
3466             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3467             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3468             HeapFree( GetProcessHeap(), 0, newstr );
3469         }
3470         return ret;
3471     }
3472     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3473 }
3474
3475
3476 /*******************************************************************
3477  *         AppendMenuA    (USER32.@)
3478  */
3479 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3480                          UINT_PTR id, LPCSTR data )
3481 {
3482     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3483 }
3484
3485
3486 /*******************************************************************
3487  *         AppendMenuW    (USER32.@)
3488  */
3489 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3490                          UINT_PTR id, LPCWSTR data )
3491 {
3492     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3493 }
3494
3495
3496 /**********************************************************************
3497  *         RemoveMenu    (USER32.@)
3498  */
3499 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3500 {
3501     LPPOPUPMENU menu;
3502     MENUITEM *item;
3503
3504     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3505     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3506     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3507
3508       /* Remove item */
3509
3510     MENU_FreeItemData( item );
3511
3512     if (--menu->nItems == 0)
3513     {
3514         HeapFree( GetProcessHeap(), 0, menu->items );
3515         menu->items = NULL;
3516     }
3517     else
3518     {
3519         while(nPos < menu->nItems)
3520         {
3521             *item = *(item+1);
3522             item++;
3523             nPos++;
3524         }
3525         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3526                                    menu->nItems * sizeof(MENUITEM) );
3527     }
3528     return TRUE;
3529 }
3530
3531
3532 /**********************************************************************
3533  *         DeleteMenu    (USER32.@)
3534  */
3535 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3536 {
3537     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3538     if (!item) return FALSE;
3539     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3540       /* nPos is now the position of the item */
3541     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3542     return TRUE;
3543 }
3544
3545
3546 /*******************************************************************
3547  *         ModifyMenuW    (USER32.@)
3548  */
3549 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3550                          UINT_PTR id, LPCWSTR str )
3551 {
3552     MENUITEM *item;
3553
3554     if (IS_STRING_ITEM(flags))
3555     {
3556         TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3557         if (!str) return FALSE;
3558     }
3559     else
3560     {
3561         TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3562     }
3563
3564     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3565     return MENU_SetItemData( item, flags, id, str );
3566 }
3567
3568
3569 /*******************************************************************
3570  *         ModifyMenuA    (USER32.@)
3571  */
3572 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3573                          UINT_PTR id, LPCSTR str )
3574 {
3575     BOOL ret = FALSE;
3576
3577     if (IS_STRING_ITEM(flags) && str)
3578     {
3579         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3580         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3581         if (newstr)
3582         {
3583             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3584             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3585             HeapFree( GetProcessHeap(), 0, newstr );
3586         }
3587         return ret;
3588     }
3589     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3590 }
3591
3592
3593 /**********************************************************************
3594  *         CreatePopupMenu    (USER32.@)
3595  */
3596 HMENU WINAPI CreatePopupMenu(void)
3597 {
3598     HMENU hmenu;
3599     POPUPMENU *menu;
3600
3601     if (!(hmenu = CreateMenu())) return 0;
3602     menu = MENU_GetMenu( hmenu );
3603     menu->wFlags |= MF_POPUP;
3604     menu->bTimeToHide = FALSE;
3605     return hmenu;
3606 }
3607
3608
3609 /**********************************************************************
3610  *         GetMenuCheckMarkDimensions    (USER.417)
3611  *         GetMenuCheckMarkDimensions    (USER32.@)
3612  */
3613 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3614 {
3615     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3616 }
3617
3618
3619 /**********************************************************************
3620  *         SetMenuItemBitmaps    (USER32.@)
3621  */
3622 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3623                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3624 {
3625     MENUITEM *item;
3626     TRACE("(%p, %04x, %04x, %p, %p)\n",
3627           hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3628     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3629
3630     if (!hNewCheck && !hNewUnCheck)
3631     {
3632         item->fState &= ~MF_USECHECKBITMAPS;
3633     }
3634     else  /* Install new bitmaps */
3635     {
3636         item->hCheckBit = hNewCheck;
3637         item->hUnCheckBit = hNewUnCheck;
3638         item->fState |= MF_USECHECKBITMAPS;
3639     }
3640     return TRUE;
3641 }
3642
3643
3644 /**********************************************************************
3645  *         CreateMenu    (USER32.@)
3646  */
3647 HMENU WINAPI CreateMenu(void)
3648 {
3649     HMENU hMenu;
3650     LPPOPUPMENU menu;
3651     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3652     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3653
3654     ZeroMemory(menu, sizeof(POPUPMENU));
3655     menu->wMagic = MENU_MAGIC;
3656     menu->FocusedItem = NO_SELECTED_ITEM;
3657     menu->bTimeToHide = FALSE;
3658
3659     TRACE("return %p\n", hMenu );
3660
3661     return hMenu;
3662 }
3663
3664
3665 /**********************************************************************
3666  *         DestroyMenu    (USER32.@)
3667  */
3668 BOOL WINAPI DestroyMenu( HMENU hMenu )
3669 {
3670     TRACE("(%p)\n", hMenu);
3671
3672     /* Silently ignore attempts to destroy default system popup */
3673
3674     if (hMenu && hMenu != MENU_DefSysPopup)
3675     {
3676         LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3677
3678         if (!lppop) return FALSE;
3679
3680         lppop->wMagic = 0;  /* Mark it as destroyed */
3681
3682         if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3683         {
3684             DestroyWindow( lppop->hWnd );
3685             lppop->hWnd = 0;
3686         }
3687
3688         if (lppop->items) /* recursively destroy submenus */
3689         {
3690             int i;
3691             MENUITEM *item = lppop->items;
3692             for (i = lppop->nItems; i > 0; i--, item++)
3693             {
3694                 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3695                 MENU_FreeItemData( item );
3696             }
3697             HeapFree( GetProcessHeap(), 0, lppop->items );
3698         }
3699         USER_HEAP_FREE( hMenu );
3700     }
3701     return (hMenu != MENU_DefSysPopup);
3702 }
3703
3704
3705 /**********************************************************************
3706  *         GetSystemMenu    (USER32.@)
3707  */
3708 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3709 {
3710     WND *wndPtr = WIN_FindWndPtr( hWnd );
3711     HMENU retvalue = 0;
3712
3713     if (wndPtr)
3714     {
3715         if( wndPtr->hSysMenu )
3716         {
3717             if( bRevert )
3718             {
3719                 DestroyMenu(wndPtr->hSysMenu);
3720                 wndPtr->hSysMenu = 0;
3721             }
3722             else
3723             {
3724                 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3725                 if( menu )
3726                 {
3727                    if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3728                       menu->items[0].hSubMenu = MENU_CopySysPopup();
3729                 }
3730                 else
3731                 {
3732                    WARN("Current sys-menu (%p) of wnd %p is broken\n",
3733                         wndPtr->hSysMenu, hWnd);
3734                    wndPtr->hSysMenu = 0;
3735                 }
3736             }
3737         }
3738
3739         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3740             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3741
3742         if( wndPtr->hSysMenu )
3743         {
3744             POPUPMENU *menu;
3745             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3746
3747             /* Store the dummy sysmenu handle to facilitate the refresh */
3748             /* of the close button if the SC_CLOSE item change */
3749             menu = MENU_GetMenu(retvalue);
3750             if ( menu )
3751                menu->hSysMenuOwner = wndPtr->hSysMenu;
3752         }
3753         WIN_ReleaseWndPtr(wndPtr);
3754     }
3755     return bRevert ? 0 : retvalue;
3756 }
3757
3758
3759 /*******************************************************************
3760  *         SetSystemMenu    (USER32.@)
3761  */
3762 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3763 {
3764     WND *wndPtr = WIN_FindWndPtr(hwnd);
3765
3766     if (wndPtr)
3767     {
3768         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3769         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3770         WIN_ReleaseWndPtr(wndPtr);
3771         return TRUE;
3772     }
3773     return FALSE;
3774 }
3775
3776
3777 /**********************************************************************
3778  *         GetMenu    (USER32.@)
3779  */
3780 HMENU WINAPI GetMenu( HWND hWnd )
3781 {
3782     HMENU retvalue = (HMENU)GetWindowLongA( hWnd, GWL_ID );
3783     TRACE("for %p returning %p\n", hWnd, retvalue);
3784     return retvalue;
3785 }
3786
3787
3788 /**********************************************************************
3789  *         SetMenu    (USER32.@)
3790  */
3791 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3792 {
3793     TRACE("(%p, %p);\n", hWnd, hMenu);
3794
3795     if (hMenu && !IsMenu(hMenu))
3796     {
3797         WARN("hMenu %p is not a menu handle\n", hMenu);
3798         return FALSE;
3799     }
3800     if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3801
3802     hWnd = WIN_GetFullHandle( hWnd );
3803     if (GetCapture() == hWnd) MENU_SetCapture(0);  /* release the capture */
3804
3805     if (hMenu != 0)
3806     {
3807         LPPOPUPMENU lpmenu;
3808
3809         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3810
3811         lpmenu->hWnd = hWnd;
3812         lpmenu->Height = 0;  /* Make sure we recalculate the size */
3813     }
3814     SetWindowLongA( hWnd, GWL_ID, (LONG_PTR)hMenu );
3815
3816     if (IsWindowVisible(hWnd))
3817         SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3818                       SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3819     return TRUE;
3820 }
3821
3822
3823
3824 /**********************************************************************
3825  *         GetSubMenu    (USER32.@)
3826  */
3827 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3828 {
3829     MENUITEM * lpmi;
3830
3831     if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3832     if (!(lpmi->fType & MF_POPUP)) return 0;
3833     return lpmi->hSubMenu;
3834 }
3835
3836
3837 /**********************************************************************
3838  *         DrawMenuBar    (USER32.@)
3839  */
3840 BOOL WINAPI DrawMenuBar( HWND hWnd )
3841 {
3842     LPPOPUPMENU lppop;
3843     HMENU hMenu = GetMenu(hWnd);
3844
3845     if (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3846     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3847
3848     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3849     lppop->hwndOwner = hWnd;
3850     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3851                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3852     return TRUE;
3853 }
3854
3855 /***********************************************************************
3856  *           DrawMenuBarTemp   (USER32.@)
3857  *
3858  * UNDOCUMENTED !!
3859  *
3860  * called by W98SE desk.cpl Control Panel Applet
3861  *
3862  * Not 100% sure about the param names, but close.
3863  */
3864 DWORD WINAPI DrawMenuBarTemp(HWND someHWND, HDC someHDC, LPRECT someRECT, HMENU someHMENU, HFONT someFONT)
3865 {
3866     FIXME("(%p, %p, %p, %p, %p): stub\n", someHWND, someHDC, someRECT, someHMENU, someFONT);
3867     return 0;
3868 }
3869
3870 /***********************************************************************
3871  *           EndMenu   (USER.187)
3872  *           EndMenu   (USER32.@)
3873  */
3874 void WINAPI EndMenu(void)
3875 {
3876     /* if we are in the menu code, and it is active */
3877     if (!fEndMenu && top_popup)
3878     {
3879         /* terminate the menu handling code */
3880         fEndMenu = TRUE;
3881
3882         /* needs to be posted to wakeup the internal menu handler */
3883         /* which will now terminate the menu, in the event that */
3884         /* the main window was minimized, or lost focus, so we */
3885         /* don't end up with an orphaned menu */
3886         PostMessageA( top_popup, WM_CANCELMODE, 0, 0);
3887     }
3888 }
3889
3890
3891 /***********************************************************************
3892  *           LookupMenuHandle   (USER.217)
3893  */
3894 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3895 {
3896     HMENU hmenu32 = HMENU_32(hmenu);
3897     UINT id32 = id;
3898     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3899     else return HMENU_16(hmenu32);
3900 }
3901
3902
3903 /**********************************************************************
3904  *          LoadMenu    (USER.150)
3905  */
3906 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3907 {
3908     HRSRC16 hRsrc;
3909     HGLOBAL16 handle;
3910     HMENU16 hMenu;
3911
3912     if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3913     if (!name) return 0;
3914
3915     instance = GetExePtr( instance );
3916     if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
3917     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3918     hMenu = LoadMenuIndirect16(LockResource16(handle));
3919     FreeResource16( handle );
3920     return hMenu;
3921 }
3922
3923
3924 /*****************************************************************
3925  *        LoadMenuA   (USER32.@)
3926  */
3927 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3928 {
3929     HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
3930     if (!hrsrc) return 0;
3931     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3932 }
3933
3934
3935 /*****************************************************************
3936  *        LoadMenuW   (USER32.@)
3937  */
3938 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3939 {
3940     HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
3941     if (!hrsrc) return 0;
3942     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3943 }
3944
3945
3946 /**********************************************************************
3947  *          LoadMenuIndirect    (USER.220)
3948  */
3949 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3950 {
3951     HMENU hMenu;
3952     WORD version, offset;
3953     LPCSTR p = (LPCSTR)template;
3954
3955     TRACE("(%p)\n", template );
3956     version = GET_WORD(p);
3957     p += sizeof(WORD);
3958     if (version)
3959     {
3960         WARN("version must be 0 for Win16\n" );
3961         return 0;
3962     }
3963     offset = GET_WORD(p);
3964     p += sizeof(WORD) + offset;
3965     if (!(hMenu = CreateMenu())) return 0;
3966     if (!MENU_ParseResource( p, hMenu, FALSE ))
3967     {
3968         DestroyMenu( hMenu );
3969         return 0;
3970     }
3971     return HMENU_16(hMenu);
3972 }
3973
3974
3975 /**********************************************************************
3976  *          LoadMenuIndirectA    (USER32.@)
3977  */
3978 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
3979 {
3980     HMENU hMenu;
3981     WORD version, offset;
3982     LPCSTR p = (LPCSTR)template;
3983
3984     version = GET_WORD(p);
3985     p += sizeof(WORD);
3986     TRACE("%p, ver %d\n", template, version );
3987     switch (version)
3988     {
3989       case 0: /* standard format is version of 0 */
3990         offset = GET_WORD(p);
3991         p += sizeof(WORD) + offset;
3992         if (!(hMenu = CreateMenu())) return 0;
3993         if (!MENU_ParseResource( p, hMenu, TRUE ))
3994           {
3995             DestroyMenu( hMenu );
3996             return 0;
3997           }
3998         return hMenu;
3999       case 1: /* extended format is version of 1 */
4000         offset = GET_WORD(p);
4001         p += sizeof(WORD) + offset;
4002         if (!(hMenu = CreateMenu())) return 0;
4003         if (!MENUEX_ParseResource( p, hMenu))
4004           {
4005             DestroyMenu( hMenu );
4006             return 0;
4007           }
4008         return hMenu;
4009       default:
4010         ERR("version %d not supported.\n", version);
4011         return 0;
4012     }
4013 }
4014
4015
4016 /**********************************************************************
4017  *          LoadMenuIndirectW    (USER32.@)
4018  */
4019 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4020 {
4021     /* FIXME: is there anything different between A and W? */
4022     return LoadMenuIndirectA( template );
4023 }
4024
4025
4026 /**********************************************************************
4027  *              IsMenu    (USER32.@)
4028  */
4029 BOOL WINAPI IsMenu(HMENU hmenu)
4030 {
4031     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4032     return menu != NULL;
4033 }
4034
4035 /**********************************************************************
4036  *              GetMenuItemInfo_common
4037  */
4038
4039 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4040                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4041 {
4042     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4043
4044     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4045
4046     if (!menu)
4047         return FALSE;
4048
4049     if (lpmii->fMask & MIIM_TYPE) {
4050         lpmii->fType = menu->fType;
4051         switch (MENU_ITEM_TYPE(menu->fType)) {
4052         case MF_STRING:
4053             break;  /* will be done below */
4054         case MF_OWNERDRAW:
4055         case MF_BITMAP:
4056             lpmii->dwTypeData = menu->text;
4057             /* fall through */
4058         default:
4059             lpmii->cch = 0;
4060         }
4061     }
4062
4063     /* copy the text string */
4064     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4065          (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4066     {
4067         int len;
4068         if (unicode)
4069         {
4070             len = strlenW(menu->text);
4071             if(lpmii->dwTypeData && lpmii->cch)
4072                 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4073         }
4074         else
4075         {
4076             len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4077             if(lpmii->dwTypeData && lpmii->cch)
4078                 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4079                                           (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4080                     ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4081         }
4082         /* if we've copied a substring we return its length */
4083         if(lpmii->dwTypeData && lpmii->cch)
4084         {
4085             if (lpmii->cch <= len) lpmii->cch--;
4086         }
4087         else /* return length of string */
4088             lpmii->cch = len;
4089     }
4090
4091     if (lpmii->fMask & MIIM_FTYPE)
4092         lpmii->fType = menu->fType;
4093
4094     if (lpmii->fMask & MIIM_BITMAP)
4095         lpmii->hbmpItem = menu->hbmpItem;
4096
4097     if (lpmii->fMask & MIIM_STATE)
4098         lpmii->fState = menu->fState;
4099
4100     if (lpmii->fMask & MIIM_ID)
4101         lpmii->wID = menu->wID;
4102
4103     if (lpmii->fMask & MIIM_SUBMENU)
4104         lpmii->hSubMenu = menu->hSubMenu;
4105
4106     if (lpmii->fMask & MIIM_CHECKMARKS) {
4107         lpmii->hbmpChecked = menu->hCheckBit;
4108         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4109     }
4110     if (lpmii->fMask & MIIM_DATA)
4111         lpmii->dwItemData = menu->dwItemData;
4112
4113   return TRUE;
4114 }
4115
4116 /**********************************************************************
4117  *              GetMenuItemInfoA    (USER32.@)
4118  */
4119 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4120                                   LPMENUITEMINFOA lpmii)
4121 {
4122     return GetMenuItemInfo_common (hmenu, item, bypos,
4123                                     (LPMENUITEMINFOW)lpmii, FALSE);
4124 }
4125
4126 /**********************************************************************
4127  *              GetMenuItemInfoW    (USER32.@)
4128  */
4129 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4130                                   LPMENUITEMINFOW lpmii)
4131 {
4132     return GetMenuItemInfo_common (hmenu, item, bypos,
4133                                      lpmii, TRUE);
4134 }
4135
4136
4137 /* set a menu item text from a ASCII or Unicode string */
4138 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4139 {
4140     if (!text)
4141     {
4142         menu->text = NULL;
4143         menu->fType |= MF_SEPARATOR;
4144     }
4145     else if (unicode)
4146     {
4147         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4148             strcpyW( menu->text, text );
4149     }
4150     else
4151     {
4152         LPCSTR str = (LPCSTR)text;
4153         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4154         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4155             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4156     }
4157 }
4158
4159
4160 /**********************************************************************
4161  *              SetMenuItemInfo_common
4162  */
4163
4164 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4165                                        const MENUITEMINFOW *lpmii,
4166                                        BOOL unicode)
4167 {
4168     if (!menu) return FALSE;
4169
4170     debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4171
4172     if (lpmii->fMask & MIIM_TYPE ) {
4173         /* Get rid of old string. */
4174         if (IS_STRING_ITEM(menu->fType) && menu->text) {
4175             HeapFree(GetProcessHeap(), 0, menu->text);
4176             menu->text = NULL;
4177         }
4178
4179         /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4180         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4181         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4182
4183         menu->text = lpmii->dwTypeData;
4184
4185        if (IS_STRING_ITEM(menu->fType))
4186            set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4187     }
4188
4189     if (lpmii->fMask & MIIM_FTYPE ) {
4190         /* free the string when the type is changing */
4191         if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4192             HeapFree(GetProcessHeap(), 0, menu->text);
4193             menu->text = NULL;
4194         }
4195         menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4196         menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4197         if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4198             menu->fType |= MF_SEPARATOR;
4199     }
4200
4201     if (lpmii->fMask & MIIM_STRING ) {
4202         /* free the string when used */
4203         if (IS_STRING_ITEM(menu->fType) && menu->text) {
4204             HeapFree(GetProcessHeap(), 0, menu->text);
4205             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4206         }
4207     }
4208
4209     if (lpmii->fMask & MIIM_STATE)
4210     {
4211         /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4212         menu->fState = lpmii->fState;
4213     }
4214
4215     if (lpmii->fMask & MIIM_ID)
4216         menu->wID = lpmii->wID;
4217
4218     if (lpmii->fMask & MIIM_SUBMENU) {
4219         menu->hSubMenu = lpmii->hSubMenu;
4220         if (menu->hSubMenu) {
4221             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4222             if (subMenu) {
4223                 subMenu->wFlags |= MF_POPUP;
4224                 menu->fType |= MF_POPUP;
4225             }
4226             else
4227                 /* FIXME: Return an error ? */
4228                 menu->fType &= ~MF_POPUP;
4229         }
4230         else
4231             menu->fType &= ~MF_POPUP;
4232     }
4233
4234     if (lpmii->fMask & MIIM_CHECKMARKS)
4235     {
4236         if (lpmii->fType & MFT_RADIOCHECK)
4237             menu->fType |= MFT_RADIOCHECK;
4238
4239         menu->hCheckBit = lpmii->hbmpChecked;
4240         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4241     }
4242     if (lpmii->fMask & MIIM_DATA)
4243         menu->dwItemData = lpmii->dwItemData;
4244
4245     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4246     return TRUE;
4247 }
4248
4249 /**********************************************************************
4250  *              SetMenuItemInfoA    (USER32.@)
4251  */
4252 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4253                                  const MENUITEMINFOA *lpmii)
4254 {
4255     if ((lpmii->fType & (MF_HILITE|MF_POPUP)) || (lpmii->fState)) {
4256         /* QuickTime does pass invalid data into SetMenuItemInfo.
4257          * do some of the checks Windows does.
4258          */
4259         WARN("Bad masks for type (0x%08x) or state (0x%08x)\n",
4260              lpmii->fType,lpmii->fState );
4261         return FALSE;
4262     }
4263
4264     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4265                                     (const MENUITEMINFOW *)lpmii, FALSE);
4266 }
4267
4268 /**********************************************************************
4269  *              SetMenuItemInfoW    (USER32.@)
4270  */
4271 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4272                                  const MENUITEMINFOW *lpmii)
4273 {
4274     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4275                                     lpmii, TRUE);
4276 }
4277
4278 /**********************************************************************
4279  *              SetMenuDefaultItem    (USER32.@)
4280  *
4281  */
4282 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4283 {
4284         UINT i;
4285         POPUPMENU *menu;
4286         MENUITEM *item;
4287
4288         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4289
4290         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4291
4292         /* reset all default-item flags */
4293         item = menu->items;
4294         for (i = 0; i < menu->nItems; i++, item++)
4295         {
4296             item->fState &= ~MFS_DEFAULT;
4297         }
4298
4299         /* no default item */
4300         if ( -1 == uItem)
4301         {
4302             return TRUE;
4303         }
4304
4305         item = menu->items;
4306         if ( bypos )
4307         {
4308             if ( uItem >= menu->nItems ) return FALSE;
4309             item[uItem].fState |= MFS_DEFAULT;
4310             return TRUE;
4311         }
4312         else
4313         {
4314             for (i = 0; i < menu->nItems; i++, item++)
4315             {
4316                 if (item->wID == uItem)
4317                 {
4318                      item->fState |= MFS_DEFAULT;
4319                      return TRUE;
4320                 }
4321             }
4322
4323         }
4324         return FALSE;
4325 }
4326
4327 /**********************************************************************
4328  *              GetMenuDefaultItem    (USER32.@)
4329  */
4330 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4331 {
4332         POPUPMENU *menu;
4333         MENUITEM * item;
4334         UINT i = 0;
4335
4336         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4337
4338         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4339
4340         /* find default item */
4341         item = menu->items;
4342
4343         /* empty menu */
4344         if (! item) return -1;
4345
4346         while ( !( item->fState & MFS_DEFAULT ) )
4347         {
4348             i++; item++;
4349             if  (i >= menu->nItems ) return -1;
4350         }
4351
4352         /* default: don't return disabled items */
4353         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4354
4355         /* search rekursiv when needed */
4356         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4357         {
4358             UINT ret;
4359             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4360             if ( -1 != ret ) return ret;
4361
4362             /* when item not found in submenu, return the popup item */
4363         }
4364         return ( bypos ) ? i : item->wID;
4365
4366 }
4367
4368
4369 /**********************************************************************
4370  *              InsertMenuItemA    (USER32.@)
4371  */
4372 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4373                                 const MENUITEMINFOA *lpmii)
4374 {
4375     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4376     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4377 }
4378
4379
4380 /**********************************************************************
4381  *              InsertMenuItemW    (USER32.@)
4382  */
4383 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4384                                 const MENUITEMINFOW *lpmii)
4385 {
4386     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4387     return SetMenuItemInfo_common(item, lpmii, TRUE);
4388 }
4389
4390 /**********************************************************************
4391  *              CheckMenuRadioItem    (USER32.@)
4392  */
4393
4394 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4395                                    UINT first, UINT last, UINT check,
4396                                    UINT bypos)
4397 {
4398      MENUITEM *mifirst, *milast, *micheck;
4399      HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4400
4401      TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4402
4403      mifirst = MENU_FindItem (&mfirst, &first, bypos);
4404      milast = MENU_FindItem (&mlast, &last, bypos);
4405      micheck = MENU_FindItem (&mcheck, &check, bypos);
4406
4407      if (mifirst == NULL || milast == NULL || micheck == NULL ||
4408          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4409          micheck > milast || micheck < mifirst)
4410           return FALSE;
4411
4412      while (mifirst <= milast)
4413      {
4414           if (mifirst == micheck)
4415           {
4416                mifirst->fType |= MFT_RADIOCHECK;
4417                mifirst->fState |= MFS_CHECKED;
4418           } else {
4419                mifirst->fType &= ~MFT_RADIOCHECK;
4420                mifirst->fState &= ~MFS_CHECKED;
4421           }
4422           mifirst++;
4423      }
4424
4425      return TRUE;
4426 }
4427
4428
4429 /**********************************************************************
4430  *              GetMenuItemRect    (USER32.@)
4431  *
4432  *      ATTENTION: Here, the returned values in rect are the screen
4433  *                 coordinates of the item just like if the menu was
4434  *                 always on the upper left side of the application.
4435  *
4436  */
4437 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4438                                  LPRECT rect)
4439 {
4440      POPUPMENU *itemMenu;
4441      MENUITEM *item;
4442      HWND referenceHwnd;
4443
4444      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4445
4446      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4447      referenceHwnd = hwnd;
4448
4449      if(!hwnd)
4450      {
4451          itemMenu = MENU_GetMenu(hMenu);
4452          if (itemMenu == NULL)
4453              return FALSE;
4454
4455          if(itemMenu->hWnd == 0)
4456              return FALSE;
4457          referenceHwnd = itemMenu->hWnd;
4458      }
4459
4460      if ((rect == NULL) || (item == NULL))
4461          return FALSE;
4462
4463      *rect = item->rect;
4464
4465      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4466
4467      return TRUE;
4468 }
4469
4470
4471 /**********************************************************************
4472  *              SetMenuInfo    (USER32.@)
4473  *
4474  * FIXME
4475  *      MIM_APPLYTOSUBMENUS
4476  *      actually use the items to draw the menu
4477  */
4478 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4479 {
4480     POPUPMENU *menu;
4481
4482     TRACE("(%p %p)\n", hMenu, lpmi);
4483
4484     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4485     {
4486
4487         if (lpmi->fMask & MIM_BACKGROUND)
4488             menu->hbrBack = lpmi->hbrBack;
4489
4490         if (lpmi->fMask & MIM_HELPID)
4491             menu->dwContextHelpID = lpmi->dwContextHelpID;
4492
4493         if (lpmi->fMask & MIM_MAXHEIGHT)
4494             menu->cyMax = lpmi->cyMax;
4495
4496         if (lpmi->fMask & MIM_MENUDATA)
4497             menu->dwMenuData = lpmi->dwMenuData;
4498
4499         if (lpmi->fMask & MIM_STYLE)
4500             menu->dwStyle = lpmi->dwStyle;
4501
4502         return TRUE;
4503     }
4504     return FALSE;
4505 }
4506
4507 /**********************************************************************
4508  *              GetMenuInfo    (USER32.@)
4509  *
4510  *  NOTES
4511  *      win98/NT5.0
4512  *
4513  */
4514 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4515 {   POPUPMENU *menu;
4516
4517     TRACE("(%p %p)\n", hMenu, lpmi);
4518
4519     if (lpmi && (menu = MENU_GetMenu(hMenu)))
4520     {
4521
4522         if (lpmi->fMask & MIM_BACKGROUND)
4523             lpmi->hbrBack = menu->hbrBack;
4524
4525         if (lpmi->fMask & MIM_HELPID)
4526             lpmi->dwContextHelpID = menu->dwContextHelpID;
4527
4528         if (lpmi->fMask & MIM_MAXHEIGHT)
4529             lpmi->cyMax = menu->cyMax;
4530
4531         if (lpmi->fMask & MIM_MENUDATA)
4532             lpmi->dwMenuData = menu->dwMenuData;
4533
4534         if (lpmi->fMask & MIM_STYLE)
4535             lpmi->dwStyle = menu->dwStyle;
4536
4537         return TRUE;
4538     }
4539     return FALSE;
4540 }
4541
4542
4543 /**********************************************************************
4544  *         SetMenuContextHelpId    (USER32.@)
4545  */
4546 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4547 {
4548     LPPOPUPMENU menu;
4549
4550     TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4551
4552     if ((menu = MENU_GetMenu(hMenu)))
4553     {
4554         menu->dwContextHelpID = dwContextHelpID;
4555         return TRUE;
4556     }
4557     return FALSE;
4558 }
4559
4560
4561 /**********************************************************************
4562  *         GetMenuContextHelpId    (USER32.@)
4563  */
4564 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4565 {
4566     LPPOPUPMENU menu;
4567
4568     TRACE("(%p)\n", hMenu);
4569
4570     if ((menu = MENU_GetMenu(hMenu)))
4571     {
4572         return menu->dwContextHelpID;
4573     }
4574     return 0;
4575 }
4576
4577 /**********************************************************************
4578  *         MenuItemFromPoint    (USER32.@)
4579  */
4580 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4581 {
4582     POPUPMENU *menu = MENU_GetMenu(hMenu);
4583     UINT pos;
4584     MENUITEM *item;
4585
4586     /*FIXME: Do we have to handle hWnd here? */
4587     item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4588
4589     return pos;
4590 }
4591
4592
4593 /**********************************************************************
4594  *           translate_accelerator
4595  */
4596 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4597                                    BYTE fVirt, WORD key, WORD cmd )
4598 {
4599     UINT mesg = 0;
4600
4601     if (wParam != key) return FALSE;
4602
4603     if (message == WM_CHAR)
4604     {
4605         if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4606         {
4607             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4608             goto found;
4609         }
4610     }
4611     else
4612     {
4613         if(fVirt & FVIRTKEY)
4614         {
4615             INT mask = 0;
4616             TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4617                           wParam, 0xff & HIWORD(lParam));
4618             if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4619             if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4620             if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4621             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4622             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4623         }
4624         else
4625         {
4626             if (!(lParam & 0x01000000))  /* no special_key */
4627             {
4628                 if ((fVirt & FALT) && (lParam & 0x20000000))
4629                 {                              /* ^^ ALT pressed */
4630                     TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4631                     goto found;
4632                 }
4633             }
4634         }
4635     }
4636     return FALSE;
4637
4638  found:
4639     if (message == WM_KEYUP || message == WM_SYSKEYUP)
4640         mesg = 1;
4641     else if (GetCapture())
4642         mesg = 2;
4643     else if (!IsWindowEnabled(hWnd))
4644         mesg = 3;
4645     else
4646     {
4647         HMENU hMenu, hSubMenu, hSysMenu;
4648         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4649
4650         hMenu = (GetWindowLongA( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4651         hSysMenu = get_win_sys_menu( hWnd );
4652
4653         /* find menu item and ask application to initialize it */
4654         /* 1. in the system menu */
4655         hSubMenu = hSysMenu;
4656         nPos = cmd;
4657         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4658         {
4659             SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4660             if(hSubMenu != hSysMenu)
4661             {
4662                 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4663                 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4664                 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4665             }
4666             uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4667         }
4668         else /* 2. in the window's menu */
4669         {
4670             hSubMenu = hMenu;
4671             nPos = cmd;
4672             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4673             {
4674                 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4675                 if(hSubMenu != hMenu)
4676                 {
4677                     nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4678                     TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4679                     SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4680                 }
4681                 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4682             }
4683         }
4684
4685         if (uSysStat != (UINT)-1)
4686         {
4687             if (uSysStat & (MF_DISABLED|MF_GRAYED))
4688                 mesg=4;
4689             else
4690                 mesg=WM_SYSCOMMAND;
4691         }
4692         else
4693         {
4694             if (uStat != (UINT)-1)
4695             {
4696                 if (IsIconic(hWnd))
4697                     mesg=5;
4698                 else
4699                 {
4700                     if (uStat & (MF_DISABLED|MF_GRAYED))
4701                         mesg=6;
4702                     else
4703                         mesg=WM_COMMAND;
4704                 }
4705             }
4706             else
4707                 mesg=WM_COMMAND;
4708         }
4709     }
4710
4711     if( mesg==WM_COMMAND )
4712     {
4713         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4714         SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
4715     }
4716     else if( mesg==WM_SYSCOMMAND )
4717     {
4718         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4719         SendMessageA(hWnd, mesg, cmd, 0x00010000L);
4720     }
4721     else
4722     {
4723         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
4724          *   #0: unknown (please report!)
4725          *   #1: for WM_KEYUP,WM_SYSKEYUP
4726          *   #2: mouse is captured
4727          *   #3: window is disabled
4728          *   #4: it's a disabled system menu option
4729          *   #5: it's a menu option, but window is iconic
4730          *   #6: it's a menu option, but disabled
4731          */
4732         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4733         if(mesg==0)
4734             ERR_(accel)(" unknown reason - please report!");
4735     }
4736     return TRUE;
4737 }
4738
4739 /**********************************************************************
4740  *      TranslateAccelerator      (USER32.@)
4741  *      TranslateAcceleratorA     (USER32.@)
4742  *      TranslateAcceleratorW     (USER32.@)
4743  */
4744 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
4745 {
4746     /* YES, Accel16! */
4747     LPACCEL16 lpAccelTbl;
4748     int i;
4749
4750     if (msg == NULL)
4751     {
4752         WARN_(accel)("msg null; should hang here to be win compatible\n");
4753         return 0;
4754     }
4755     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4756     {
4757         WARN_(accel)("invalid accel handle=%p\n", hAccel);
4758         return 0;
4759     }
4760     if ((msg->message != WM_KEYDOWN &&
4761          msg->message != WM_KEYUP &&
4762          msg->message != WM_SYSKEYDOWN &&
4763          msg->message != WM_SYSKEYUP &&
4764          msg->message != WM_CHAR)) return 0;
4765
4766     TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4767                   "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4768                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4769
4770     i = 0;
4771     do
4772     {
4773         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4774                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4775             return 1;
4776     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4777     WARN_(accel)("couldn't translate accelerator key\n");
4778     return 0;
4779 }