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