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