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