wined3d: Fix uploading higher transform matrices.
[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              0x10000
120 #define TF_SUSPENDPOPUP         0x20000
121 #define TF_SKIPREMOVE           0x40000
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, UINT flags,
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
1807     if( flags & TPM_RIGHTALIGN ) x -= width;
1808     if( flags & TPM_CENTERALIGN ) x -= width / 2;
1809
1810     if( flags & TPM_BOTTOMALIGN ) y -= height;
1811     if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1812
1813     if( x + width > info.rcWork.right)
1814     {
1815         if( xanchor && x >= width - xanchor )
1816             x -= width - xanchor;
1817
1818         if( x + width > info.rcWork.right)
1819             x = info.rcWork.right - width;
1820     }
1821     if( x < info.rcWork.left ) x = info.rcWork.left;
1822
1823     if( y + height > info.rcWork.bottom)
1824     {
1825         if( yanchor && y >= height + yanchor )
1826             y -= height + yanchor;
1827
1828         if( y + height > info.rcWork.bottom)
1829             y = info.rcWork.bottom - height;
1830     }
1831     if( y < info.rcWork.top ) y = info.rcWork.top;
1832
1833     /* NOTE: In Windows, top menu popup is not owned. */
1834     menu->hWnd = CreateWindowExW( 0, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1835                                 WS_POPUP, x, y, width, height,
1836                                 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1837                                 (LPVOID)hmenu );
1838     if( !menu->hWnd ) return FALSE;
1839     if (!top_popup) top_popup = menu->hWnd;
1840
1841     /* Display the window */
1842
1843     SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1844                   SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1845     UpdateWindow( menu->hWnd );
1846     return TRUE;
1847 }
1848
1849
1850 /***********************************************************************
1851  *           MENU_EnsureMenuItemVisible
1852  */
1853 static void
1854 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1855 {
1856     if (lppop->bScrolling)
1857     {
1858         MENUITEM *item = &lppop->items[wIndex];
1859         UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1860         UINT nOldPos = lppop->nScrollPos;
1861         RECT rc;
1862         UINT arrow_bitmap_height;
1863         BITMAP bmp;
1864         
1865         GetClientRect(lppop->hWnd, &rc);
1866
1867         GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1868         arrow_bitmap_height = bmp.bmHeight;
1869
1870         rc.top += arrow_bitmap_height;
1871         rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1872        
1873         nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1874         if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1875         {
1876             
1877             lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1878             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1879             MENU_DrawScrollArrows(lppop, hdc);
1880         }
1881         else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1882         {
1883             lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1884             ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1885             MENU_DrawScrollArrows(lppop, hdc);
1886         }
1887     }
1888 }
1889
1890
1891 /***********************************************************************
1892  *           MENU_SelectItem
1893  */
1894 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1895                              BOOL sendMenuSelect, HMENU topmenu )
1896 {
1897     LPPOPUPMENU lppop;
1898     HDC hdc;
1899
1900     TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1901
1902     lppop = MENU_GetMenu( hmenu );
1903     if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1904
1905     if (lppop->FocusedItem == wIndex) return;
1906     if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1907     else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1908     if (!top_popup) top_popup = lppop->hWnd;
1909
1910     SelectObject( hdc, get_menu_font(FALSE));
1911
1912       /* Clear previous highlighted item */
1913     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1914     {
1915         lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1916         MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1917                           lppop->Height, !(lppop->wFlags & MF_POPUP),
1918                           ODA_SELECT );
1919     }
1920
1921       /* Highlight new item (if any) */
1922     lppop->FocusedItem = wIndex;
1923     if (lppop->FocusedItem != NO_SELECTED_ITEM)
1924     {
1925         if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1926             lppop->items[wIndex].fState |= MF_HILITE;
1927             MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1928             MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1929                     &lppop->items[wIndex], lppop->Height,
1930                     !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1931         }
1932         if (sendMenuSelect)
1933         {
1934             MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1935             SendMessageW( hwndOwner, WM_MENUSELECT,
1936                      MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1937                      ip->fType | ip->fState |
1938                      (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1939         }
1940     }
1941     else if (sendMenuSelect) {
1942         if(topmenu){
1943             int pos;
1944             if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1945                 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1946                 MENUITEM *ip = &ptm->items[pos];
1947                 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1948                          ip->fType | ip->fState |
1949                          (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1950             }
1951         }
1952     }
1953     ReleaseDC( lppop->hWnd, hdc );
1954 }
1955
1956
1957 /***********************************************************************
1958  *           MENU_MoveSelection
1959  *
1960  * Moves currently selected item according to the offset parameter.
1961  * If there is no selection then it should select the last item if
1962  * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1963  */
1964 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1965 {
1966     INT i;
1967     POPUPMENU *menu;
1968
1969     TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1970
1971     menu = MENU_GetMenu( hmenu );
1972     if ((!menu) || (!menu->items)) return;
1973
1974     if ( menu->FocusedItem != NO_SELECTED_ITEM )
1975     {
1976         if( menu->nItems == 1 ) return; else
1977         for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1978                                             ; i += offset)
1979             if (!(menu->items[i].fType & MF_SEPARATOR))
1980             {
1981                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1982                 return;
1983             }
1984     }
1985
1986     for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1987                   i >= 0 && i < menu->nItems ; i += offset)
1988         if (!(menu->items[i].fType & MF_SEPARATOR))
1989         {
1990             MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1991             return;
1992         }
1993 }
1994
1995
1996 /**********************************************************************
1997  *         MENU_SetItemData
1998  *
1999  * Set an item's flags, id and text ptr. Called by InsertMenu() and
2000  * ModifyMenu().
2001  */
2002 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
2003                                 LPCWSTR str )
2004 {
2005     debug_print_menuitem("MENU_SetItemData from: ", item, "");
2006     TRACE("flags=%x str=%p\n", flags, str);
2007
2008     if (IS_STRING_ITEM(flags))
2009     {
2010         LPWSTR prevText = item->text;
2011         if (!str)
2012         {
2013             flags |= MF_SEPARATOR;
2014             item->text = NULL;
2015         }
2016         else
2017         {
2018             LPWSTR text;
2019             /* Item beginning with a backspace is a help item */
2020             if (*str == '\b')
2021             {
2022                 flags |= MF_HELP;
2023                 str++;
2024             }
2025             if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2026                 return FALSE;
2027             strcpyW( text, str );
2028             item->text = text;
2029         }
2030         item->hbmpItem = NULL;
2031         HeapFree( GetProcessHeap(), 0, prevText );
2032     }
2033     else if(( flags & MFT_BITMAP)) {
2034         item->hbmpItem = HBITMAP_32(LOWORD(str));
2035         /* setting bitmap clears text */
2036         HeapFree( GetProcessHeap(), 0, item->text );
2037         item->text = NULL;
2038     }
2039
2040     if (flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
2041
2042     if (flags & MF_OWNERDRAW)
2043         item->dwItemData = (DWORD_PTR)str;
2044     else
2045         item->dwItemData = 0;
2046
2047     if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2048         DestroyMenu( item->hSubMenu );   /* ModifyMenu() spec */
2049
2050     if (flags & MF_POPUP)
2051     {
2052         POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2053         if (menu) menu->wFlags |= MF_POPUP;
2054         else
2055         {
2056             item->wID = 0;
2057             item->hSubMenu = 0;
2058             item->fType = 0;
2059             item->fState = 0;
2060             return FALSE;
2061         }
2062     }
2063
2064     item->wID = id;
2065     if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2066
2067     if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2068       flags |= MF_POPUP; /* keep popup */
2069
2070     item->fType = flags & TYPE_MASK;
2071     /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2072        for ModifyMenu, but Windows accepts it */
2073     item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2074
2075     /* Don't call SetRectEmpty here! */
2076
2077     debug_print_menuitem("MENU_SetItemData to  : ", item, "");
2078     return TRUE;
2079 }
2080
2081
2082 /**********************************************************************
2083  *         MENU_InsertItem
2084  *
2085  * Insert (allocate) a new item into a menu.
2086  */
2087 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2088 {
2089     MENUITEM *newItems;
2090     POPUPMENU *menu;
2091
2092     if (!(menu = MENU_GetMenu(hMenu)))
2093         return NULL;
2094
2095     /* Find where to insert new item */
2096
2097     if (flags & MF_BYPOSITION) {
2098         if (pos > menu->nItems)
2099             pos = menu->nItems;
2100     } else {
2101         if (!MENU_FindItem( &hMenu, &pos, flags ))
2102             pos = menu->nItems;
2103         else {
2104             if (!(menu = MENU_GetMenu( hMenu )))
2105                 return NULL;
2106         }
2107     }
2108
2109     /* Make sure that MDI system buttons stay on the right side.
2110      * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2111      * regardless of their id.
2112      */
2113     while (pos > 0 && (menu->items[pos - 1].fType & MFT_BITMAP) &&
2114            (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2115            (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2116         pos--;
2117
2118     TRACE("inserting at %u by pos %u\n", pos, flags & MF_BYPOSITION);
2119
2120     /* Create new items array */
2121
2122     newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2123     if (!newItems)
2124     {
2125         WARN("allocation failed\n" );
2126         return NULL;
2127     }
2128     if (menu->nItems > 0)
2129     {
2130           /* Copy the old array into the new one */
2131         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2132         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2133                                         (menu->nItems-pos)*sizeof(MENUITEM) );
2134         HeapFree( GetProcessHeap(), 0, menu->items );
2135     }
2136     menu->items = newItems;
2137     menu->nItems++;
2138     memset( &newItems[pos], 0, sizeof(*newItems) );
2139     menu->Height = 0; /* force size recalculate */
2140     return &newItems[pos];
2141 }
2142
2143
2144 /**********************************************************************
2145  *         MENU_ParseResource
2146  *
2147  * Parse a standard menu resource and add items to the menu.
2148  * Return a pointer to the end of the resource.
2149  *
2150  * NOTE: flags is equivalent to the mtOption field
2151  */
2152 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2153 {
2154     WORD flags, id = 0;
2155     LPCSTR str;
2156     BOOL end_flag;
2157
2158     do
2159     {
2160         flags = GET_WORD(res);
2161         end_flag = flags & MF_END;
2162         /* Remove MF_END because it has the same value as MF_HILITE */
2163         flags &= ~MF_END;
2164         res += sizeof(WORD);
2165         if (!(flags & MF_POPUP))
2166         {
2167             id = GET_WORD(res);
2168             res += sizeof(WORD);
2169         }
2170         str = res;
2171         if (!unicode) res += strlen(str) + 1;
2172         else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2173         if (flags & MF_POPUP)
2174         {
2175             HMENU hSubMenu = CreatePopupMenu();
2176             if (!hSubMenu) return NULL;
2177             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2178                 return NULL;
2179             if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2180             else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2181         }
2182         else  /* Not a popup */
2183         {
2184             if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2185             else AppendMenuW( hMenu, flags, id,
2186                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2187         }
2188     } while (!end_flag);
2189     return res;
2190 }
2191
2192
2193 /**********************************************************************
2194  *         MENUEX_ParseResource
2195  *
2196  * Parse an extended menu resource and add items to the menu.
2197  * Return a pointer to the end of the resource.
2198  */
2199 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2200 {
2201     WORD resinfo;
2202     do {
2203         MENUITEMINFOW mii;
2204
2205         mii.cbSize = sizeof(mii);
2206         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2207         mii.fType = GET_DWORD(res);
2208         res += sizeof(DWORD);
2209         mii.fState = GET_DWORD(res);
2210         res += sizeof(DWORD);
2211         mii.wID = GET_DWORD(res);
2212         res += sizeof(DWORD);
2213         resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
2214         res += sizeof(WORD);
2215         /* Align the text on a word boundary.  */
2216         res += (~((UINT_PTR)res - 1)) & 1;
2217         mii.dwTypeData = (LPWSTR) res;
2218         res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2219         /* Align the following fields on a dword boundary.  */
2220         res += (~((UINT_PTR)res - 1)) & 3;
2221
2222         TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2223               mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2224
2225         if (resinfo & 1) {      /* Pop-up? */
2226             /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
2227             res += sizeof(DWORD);
2228             mii.hSubMenu = CreatePopupMenu();
2229             if (!mii.hSubMenu)
2230                 return NULL;
2231             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2232                 DestroyMenu(mii.hSubMenu);
2233                 return NULL;
2234             }
2235             mii.fMask |= MIIM_SUBMENU;
2236             mii.fType |= MF_POPUP;
2237         }
2238         else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2239         {
2240             WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2241                 mii.wID, mii.fType);
2242             mii.fType |= MF_SEPARATOR;
2243         }
2244         InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2245     } while (!(resinfo & MF_END));
2246     return res;
2247 }
2248
2249
2250 /***********************************************************************
2251  *           MENU_GetSubPopup
2252  *
2253  * Return the handle of the selected sub-popup menu (if any).
2254  */
2255 static HMENU MENU_GetSubPopup( HMENU hmenu )
2256 {
2257     POPUPMENU *menu;
2258     MENUITEM *item;
2259
2260     menu = MENU_GetMenu( hmenu );
2261
2262     if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2263
2264     item = &menu->items[menu->FocusedItem];
2265     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2266         return item->hSubMenu;
2267     return 0;
2268 }
2269
2270
2271 /***********************************************************************
2272  *           MENU_HideSubPopups
2273  *
2274  * Hide the sub-popup menus of this menu.
2275  */
2276 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2277                                 BOOL sendMenuSelect, UINT wFlags )
2278 {
2279     POPUPMENU *menu = MENU_GetMenu( hmenu );
2280
2281     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2282
2283     if (menu && top_popup)
2284     {
2285         HMENU hsubmenu;
2286         POPUPMENU *submenu;
2287         MENUITEM *item;
2288
2289         if (menu->FocusedItem != NO_SELECTED_ITEM)
2290         {
2291             item = &menu->items[menu->FocusedItem];
2292             if (!(item->fType & MF_POPUP) ||
2293                 !(item->fState & MF_MOUSESELECT)) return;
2294             item->fState &= ~MF_MOUSESELECT;
2295             hsubmenu = item->hSubMenu;
2296         } else return;
2297
2298         submenu = MENU_GetMenu( hsubmenu );
2299         MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2300         MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2301         DestroyWindow( submenu->hWnd );
2302         submenu->hWnd = 0;
2303
2304         if (!(wFlags & TPM_NONOTIFY))
2305            SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2306                          MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2307     }
2308 }
2309
2310
2311 /***********************************************************************
2312  *           MENU_ShowSubPopup
2313  *
2314  * Display the sub-menu of the selected item of this menu.
2315  * Return the handle of the submenu, or hmenu if no submenu to display.
2316  */
2317 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2318                                   BOOL selectFirst, UINT wFlags )
2319 {
2320     RECT rect;
2321     POPUPMENU *menu;
2322     MENUITEM *item;
2323     HDC hdc;
2324
2325     TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2326
2327     if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2328
2329     if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2330
2331     item = &menu->items[menu->FocusedItem];
2332     if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2333         return hmenu;
2334
2335     /* message must be sent before using item,
2336        because nearly everything may be changed by the application ! */
2337
2338     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2339     if (!(wFlags & TPM_NONOTIFY))
2340        SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2341                      MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2342
2343     item = &menu->items[menu->FocusedItem];
2344     rect = item->rect;
2345
2346     /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2347     if (!(item->fState & MF_HILITE))
2348     {
2349         if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2350         else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2351
2352         SelectObject( hdc, get_menu_font(FALSE));
2353
2354         item->fState |= MF_HILITE;
2355         MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2356         ReleaseDC( menu->hWnd, hdc );
2357     }
2358     if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2359       item->rect = rect;
2360
2361     item->fState |= MF_MOUSESELECT;
2362
2363     if (IS_SYSTEM_MENU(menu))
2364     {
2365         MENU_InitSysMenuPopup(item->hSubMenu,
2366                               GetWindowLongW( menu->hWnd, GWL_STYLE ),
2367                               GetClassLongW( menu->hWnd, GCL_STYLE));
2368
2369         NC_GetSysPopupPos( menu->hWnd, &rect );
2370         rect.top = rect.bottom;
2371         rect.right = GetSystemMetrics(SM_CXSIZE);
2372         rect.bottom = GetSystemMetrics(SM_CYSIZE);
2373     }
2374     else
2375     {
2376         GetWindowRect( menu->hWnd, &rect );
2377         if (menu->wFlags & MF_POPUP)
2378         {
2379             RECT rc = item->rect;
2380
2381             MENU_AdjustMenuItemRect(menu, &rc);
2382
2383             /* The first item in the popup menu has to be at the
2384                same y position as the focused menu item */
2385             rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2386             rect.top += rc.top - MENU_TOP_MARGIN;
2387             rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2388             rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2389                           - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2390         }
2391         else
2392         {
2393             rect.left += item->rect.left;
2394             rect.top += item->rect.bottom;
2395             rect.right = item->rect.right - item->rect.left;
2396             rect.bottom = item->rect.bottom - item->rect.top;
2397         }
2398     }
2399
2400     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, 0,
2401                     rect.left, rect.top, rect.right, rect.bottom );
2402     if (selectFirst)
2403         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2404     return item->hSubMenu;
2405 }
2406
2407
2408
2409 /**********************************************************************
2410  *         MENU_IsMenuActive
2411  */
2412 HWND MENU_IsMenuActive(void)
2413 {
2414     return top_popup;
2415 }
2416
2417 /***********************************************************************
2418  *           MENU_PtMenu
2419  *
2420  * Walks menu chain trying to find a menu pt maps to.
2421  */
2422 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2423 {
2424    POPUPMENU *menu = MENU_GetMenu( hMenu );
2425    UINT item = menu->FocusedItem;
2426    HMENU ret;
2427
2428    /* try subpopup first (if any) */
2429    ret = (item != NO_SELECTED_ITEM &&
2430           (menu->items[item].fType & MF_POPUP) &&
2431           (menu->items[item].fState & MF_MOUSESELECT))
2432         ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2433
2434    if (!ret)  /* check the current window (avoiding WM_HITTEST) */
2435    {
2436        INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2437        if( menu->wFlags & MF_POPUP )
2438        {
2439            if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2440        }
2441        else if (ht == HTSYSMENU)
2442            ret = get_win_sys_menu( menu->hWnd );
2443        else if (ht == HTMENU)
2444            ret = GetMenu( menu->hWnd );
2445    }
2446    return ret;
2447 }
2448
2449 /***********************************************************************
2450  *           MENU_ExecFocusedItem
2451  *
2452  * Execute a menu item (for instance when user pressed Enter).
2453  * Return the wID of the executed item. Otherwise, -1 indicating
2454  * that no menu item was executed, -2 if a popup is shown;
2455  * Have to receive the flags for the TrackPopupMenu options to avoid
2456  * sending unwanted message.
2457  *
2458  */
2459 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2460 {
2461     MENUITEM *item;
2462     POPUPMENU *menu = MENU_GetMenu( hMenu );
2463
2464     TRACE("%p hmenu=%p\n", pmt, hMenu);
2465
2466     if (!menu || !menu->nItems ||
2467         (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2468
2469     item = &menu->items[menu->FocusedItem];
2470
2471     TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2472
2473     if (!(item->fType & MF_POPUP))
2474     {
2475         if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2476         {
2477             /* If TPM_RETURNCMD is set you return the id, but
2478                do not send a message to the owner */
2479             if(!(wFlags & TPM_RETURNCMD))
2480             {
2481                 if( menu->wFlags & MF_SYSMENU )
2482                     PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2483                                   MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2484                 else
2485                 {
2486                     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2487                     DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2488
2489                     if (dwStyle & MNS_NOTIFYBYPOS)
2490                         PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2491                                       (LPARAM)hMenu);
2492                     else
2493                         PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2494                 }
2495             }
2496             return item->wID;
2497         }
2498     }
2499     else
2500     {
2501         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2502         return -2;
2503     }
2504
2505     return -1;
2506 }
2507
2508 /***********************************************************************
2509  *           MENU_SwitchTracking
2510  *
2511  * Helper function for menu navigation routines.
2512  */
2513 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2514 {
2515     POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2516     POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2517
2518     TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2519
2520     if( pmt->hTopMenu != hPtMenu &&
2521         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2522     {
2523         /* both are top level menus (system and menu-bar) */
2524         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2525         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2526         pmt->hTopMenu = hPtMenu;
2527     }
2528     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2529     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2530 }
2531
2532
2533 /***********************************************************************
2534  *           MENU_ButtonDown
2535  *
2536  * Return TRUE if we can go on with menu tracking.
2537  */
2538 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2539 {
2540     TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2541
2542     if (hPtMenu)
2543     {
2544         UINT id = 0;
2545         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2546         MENUITEM *item;
2547
2548         if( IS_SYSTEM_MENU(ptmenu) )
2549             item = ptmenu->items;
2550         else
2551             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2552
2553         if( item )
2554         {
2555             if( ptmenu->FocusedItem != id )
2556                 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2557
2558             /* If the popup menu is not already "popped" */
2559             if(!(item->fState & MF_MOUSESELECT ))
2560             {
2561                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2562             }
2563
2564             return TRUE;
2565         }
2566         /* Else the click was on the menu bar, finish the tracking */
2567     }
2568     return FALSE;
2569 }
2570
2571 /***********************************************************************
2572  *           MENU_ButtonUp
2573  *
2574  * Return the value of MENU_ExecFocusedItem if
2575  * the selected item was not a popup. Else open the popup.
2576  * A -1 return value indicates that we go on with menu tracking.
2577  *
2578  */
2579 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2580 {
2581     TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2582
2583     if (hPtMenu)
2584     {
2585         UINT id = 0;
2586         POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2587         MENUITEM *item;
2588
2589         if( IS_SYSTEM_MENU(ptmenu) )
2590             item = ptmenu->items;
2591         else
2592             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2593
2594         if( item && (ptmenu->FocusedItem == id ))
2595         {
2596             debug_print_menuitem ("FocusedItem: ", item, "");
2597
2598             if( !(item->fType & MF_POPUP) )
2599             {
2600                 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2601                 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2602                 return executedMenuId;
2603             }
2604
2605             /* If we are dealing with the top-level menu            */
2606             /* and this is a click on an already "popped" item:     */
2607             /* Stop the menu tracking and close the opened submenus */
2608             if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2609                 return 0;
2610         }
2611         ptmenu->bTimeToHide = TRUE;
2612     }
2613     return -1;
2614 }
2615
2616
2617 /***********************************************************************
2618  *           MENU_MouseMove
2619  *
2620  * Return TRUE if we can go on with menu tracking.
2621  */
2622 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2623 {
2624     UINT id = NO_SELECTED_ITEM;
2625     POPUPMENU *ptmenu = NULL;
2626
2627     if( hPtMenu )
2628     {
2629         ptmenu = MENU_GetMenu( hPtMenu );
2630         if( IS_SYSTEM_MENU(ptmenu) )
2631             id = 0;
2632         else
2633             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2634     }
2635
2636     if( id == NO_SELECTED_ITEM )
2637     {
2638         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2639                          NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2640
2641     }
2642     else if( ptmenu->FocusedItem != id )
2643     {
2644             MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2645             pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2646     }
2647     return TRUE;
2648 }
2649
2650
2651 /***********************************************************************
2652  *           MENU_DoNextMenu
2653  *
2654  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2655  */
2656 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2657 {
2658     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2659     BOOL atEnd = FALSE;
2660
2661     /* When skipping left, we need to do something special after the
2662        first menu.                                                  */
2663     if (vk == VK_LEFT && menu->FocusedItem == 0)
2664     {
2665         atEnd = TRUE;
2666     }
2667     /* When skipping right, for the non-system menu, we need to
2668        handle the last non-special menu item (ie skip any window
2669        icons such as MDI maximize, restore or close)             */
2670     else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2671     {
2672         int i = menu->FocusedItem + 1;
2673         while (i < menu->nItems) {
2674             if ((menu->items[i].wID >= SC_SIZE &&
2675                  menu->items[i].wID <= SC_RESTORE)) {
2676                 i++;
2677             } else break;
2678         }
2679         if (i == menu->nItems) {
2680             atEnd = TRUE;
2681         }
2682     }
2683     /* When skipping right, we need to cater for the system menu */
2684     else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2685     {
2686         if (menu->FocusedItem == (menu->nItems - 1)) {
2687             atEnd = TRUE;
2688         }
2689     }
2690
2691     if( atEnd )
2692     {
2693         MDINEXTMENU next_menu;
2694         HMENU hNewMenu;
2695         HWND  hNewWnd;
2696         UINT  id = 0;
2697
2698         next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2699         next_menu.hmenuNext = 0;
2700         next_menu.hwndNext = 0;
2701         SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2702
2703         TRACE("%p [%p] -> %p [%p]\n",
2704               pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2705
2706         if (!next_menu.hmenuNext || !next_menu.hwndNext)
2707         {
2708             DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2709             hNewWnd = pmt->hOwnerWnd;
2710             if( IS_SYSTEM_MENU(menu) )
2711             {
2712                 /* switch to the menu bar */
2713
2714                 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2715
2716                 if( vk == VK_LEFT )
2717                 {
2718                     menu = MENU_GetMenu( hNewMenu );
2719                     id = menu->nItems - 1;
2720
2721                     /* Skip backwards over any system predefined icons,
2722                        eg. MDI close, restore etc icons                 */
2723                     while ((id > 0) &&
2724                            (menu->items[id].wID >= SC_SIZE &&
2725                             menu->items[id].wID <= SC_RESTORE)) id--;
2726                 }
2727             }
2728             else if (style & WS_SYSMENU )
2729             {
2730                 /* switch to the system menu */
2731                 hNewMenu = get_win_sys_menu( hNewWnd );
2732             }
2733             else return FALSE;
2734         }
2735         else    /* application returned a new menu to switch to */
2736         {
2737             hNewMenu = next_menu.hmenuNext;
2738             hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2739
2740             if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2741             {
2742                 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2743
2744                 if (style & WS_SYSMENU &&
2745                     GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2746                 {
2747                     /* get the real system menu */
2748                     hNewMenu =  get_win_sys_menu(hNewWnd);
2749                 }
2750                 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2751                 {
2752                     /* FIXME: Not sure what to do here;
2753                      * perhaps try to track hNewMenu as a popup? */
2754
2755                     TRACE(" -- got confused.\n");
2756                     return FALSE;
2757                 }
2758             }
2759             else return FALSE;
2760         }
2761
2762         if( hNewMenu != pmt->hTopMenu )
2763         {
2764             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2765                     FALSE, 0 );
2766             if( pmt->hCurrentMenu != pmt->hTopMenu )
2767                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2768         }
2769
2770         if( hNewWnd != pmt->hOwnerWnd )
2771         {
2772             pmt->hOwnerWnd = hNewWnd;
2773             set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2774         }
2775
2776         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2777         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2778
2779         return TRUE;
2780     }
2781     return FALSE;
2782 }
2783
2784 /***********************************************************************
2785  *           MENU_SuspendPopup
2786  *
2787  * The idea is not to show the popup if the next input message is
2788  * going to hide it anyway.
2789  */
2790 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2791 {
2792     MSG msg;
2793
2794     msg.hwnd = pmt->hOwnerWnd;
2795
2796     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2797     pmt->trackFlags |= TF_SKIPREMOVE;
2798
2799     switch( uMsg )
2800     {
2801         case WM_KEYDOWN:
2802              PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2803              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2804              {
2805                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2806                  PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2807                  if( msg.message == WM_KEYDOWN &&
2808                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2809                  {
2810                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2811                      return TRUE;
2812                  }
2813              }
2814              break;
2815     }
2816
2817     /* failures go through this */
2818     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2819     return FALSE;
2820 }
2821
2822 /***********************************************************************
2823  *           MENU_KeyEscape
2824  *
2825  * Handle a VK_ESCAPE key event in a menu.
2826  */
2827 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2828 {
2829     BOOL bEndMenu = TRUE;
2830
2831     if (pmt->hCurrentMenu != pmt->hTopMenu)
2832     {
2833         POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2834
2835         if (menu->wFlags & MF_POPUP)
2836         {
2837             HMENU hmenutmp, hmenuprev;
2838
2839             hmenuprev = hmenutmp = pmt->hTopMenu;
2840
2841             /* close topmost popup */
2842             while (hmenutmp != pmt->hCurrentMenu)
2843             {
2844                 hmenuprev = hmenutmp;
2845                 hmenutmp = MENU_GetSubPopup( hmenuprev );
2846             }
2847
2848             MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2849             pmt->hCurrentMenu = hmenuprev;
2850             bEndMenu = FALSE;
2851         }
2852     }
2853
2854     return bEndMenu;
2855 }
2856
2857 /***********************************************************************
2858  *           MENU_KeyLeft
2859  *
2860  * Handle a VK_LEFT key event in a menu.
2861  */
2862 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2863 {
2864     POPUPMENU *menu;
2865     HMENU hmenutmp, hmenuprev;
2866     UINT  prevcol;
2867
2868     hmenuprev = hmenutmp = pmt->hTopMenu;
2869     menu = MENU_GetMenu( hmenutmp );
2870
2871     /* Try to move 1 column left (if possible) */
2872     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2873         NO_SELECTED_ITEM ) {
2874
2875         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2876                          prevcol, TRUE, 0 );
2877         return;
2878     }
2879
2880     /* close topmost popup */
2881     while (hmenutmp != pmt->hCurrentMenu)
2882     {
2883         hmenuprev = hmenutmp;
2884         hmenutmp = MENU_GetSubPopup( hmenuprev );
2885     }
2886
2887     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2888     pmt->hCurrentMenu = hmenuprev;
2889
2890     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2891     {
2892         /* move menu bar selection if no more popups are left */
2893
2894         if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2895              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2896
2897         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2898         {
2899            /* A sublevel menu was displayed - display the next one
2900             * unless there is another displacement coming up */
2901
2902             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2903                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2904                                                 pmt->hTopMenu, TRUE, wFlags);
2905         }
2906     }
2907 }
2908
2909
2910 /***********************************************************************
2911  *           MENU_KeyRight
2912  *
2913  * Handle a VK_RIGHT key event in a menu.
2914  */
2915 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2916 {
2917     HMENU hmenutmp;
2918     POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2919     UINT  nextcol;
2920
2921     TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2922           pmt->hCurrentMenu,
2923           debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2924           pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2925
2926     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2927     {
2928         /* If already displaying a popup, try to display sub-popup */
2929
2930         hmenutmp = pmt->hCurrentMenu;
2931         pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2932
2933         /* if subpopup was displayed then we are done */
2934         if (hmenutmp != pmt->hCurrentMenu) return;
2935     }
2936
2937     /* Check to see if there's another column */
2938     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2939         NO_SELECTED_ITEM ) {
2940         TRACE("Going to %d.\n", nextcol );
2941         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2942                          nextcol, TRUE, 0 );
2943         return;
2944     }
2945
2946     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2947     {
2948         if( pmt->hCurrentMenu != pmt->hTopMenu )
2949         {
2950             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2951             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2952         } else hmenutmp = 0;
2953
2954         /* try to move to the next item */
2955         if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2956              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2957
2958         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2959             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2960                 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2961                                                        pmt->hTopMenu, TRUE, wFlags);
2962     }
2963 }
2964
2965 /***********************************************************************
2966  *           MENU_TrackMenu
2967  *
2968  * Menu tracking code.
2969  */
2970 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2971                             HWND hwnd, const RECT *lprect )
2972 {
2973     MSG msg;
2974     POPUPMENU *menu;
2975     BOOL fRemove;
2976     INT executedMenuId = -1;
2977     MTRACKER mt;
2978     BOOL enterIdleSent = FALSE;
2979     HWND capture_win;
2980
2981     mt.trackFlags = 0;
2982     mt.hCurrentMenu = hmenu;
2983     mt.hTopMenu = hmenu;
2984     mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2985     mt.pt.x = x;
2986     mt.pt.y = y;
2987
2988     TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2989           hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2990
2991     fEndMenu = FALSE;
2992     if (!(menu = MENU_GetMenu( hmenu )))
2993     {
2994         WARN("Invalid menu handle %p\n", hmenu);
2995         SetLastError(ERROR_INVALID_MENU_HANDLE);
2996         return FALSE;
2997     }
2998
2999     if (wFlags & TPM_BUTTONDOWN)
3000     {
3001         /* Get the result in order to start the tracking or not */
3002         fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3003         fEndMenu = !fRemove;
3004     }
3005
3006     if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3007
3008     /* owner may not be visible when tracking a popup, so use the menu itself */
3009     capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3010     set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3011
3012     while (!fEndMenu)
3013     {
3014         menu = MENU_GetMenu( mt.hCurrentMenu );
3015         if (!menu) /* sometimes happens if I do a window manager close */
3016             break;
3017
3018         /* we have to keep the message in the queue until it's
3019          * clear that menu loop is not over yet. */
3020
3021         for (;;)
3022         {
3023             if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3024             {
3025                 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3026                 /* remove the message from the queue */
3027                 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3028             }
3029             else
3030             {
3031                 if (!enterIdleSent)
3032                 {
3033                     HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
3034                     enterIdleSent = TRUE;
3035                     SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3036                 }
3037                 WaitMessage();
3038             }
3039         }
3040
3041         /* check if EndMenu() tried to cancel us, by posting this message */
3042         if(msg.message == WM_CANCELMODE)
3043         {
3044             /* we are now out of the loop */
3045             fEndMenu = TRUE;
3046
3047             /* remove the message from the queue */
3048             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3049
3050             /* break out of internal loop, ala ESCAPE */
3051             break;
3052         }
3053
3054         TranslateMessage( &msg );
3055         mt.pt = msg.pt;
3056
3057         if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3058           enterIdleSent=FALSE;
3059
3060         fRemove = FALSE;
3061         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3062         {
3063             /*
3064              * Use the mouse coordinates in lParam instead of those in the MSG
3065              * struct to properly handle synthetic messages. They are already
3066              * in screen coordinates.
3067              */
3068             mt.pt.x = (short)LOWORD(msg.lParam);
3069             mt.pt.y = (short)HIWORD(msg.lParam);
3070
3071             /* Find a menu for this mouse event */
3072             hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3073
3074             switch(msg.message)
3075             {
3076                 /* no WM_NC... messages in captured state */
3077
3078                 case WM_RBUTTONDBLCLK:
3079                 case WM_RBUTTONDOWN:
3080                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3081                     /* fall through */
3082                 case WM_LBUTTONDBLCLK:
3083                 case WM_LBUTTONDOWN:
3084                     /* If the message belongs to the menu, removes it from the queue */
3085                     /* Else, end menu tracking */
3086                     fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3087                     fEndMenu = !fRemove;
3088                     break;
3089
3090                 case WM_RBUTTONUP:
3091                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
3092                     /* fall through */
3093                 case WM_LBUTTONUP:
3094                     /* Check if a menu was selected by the mouse */
3095                     if (hmenu)
3096                     {
3097                         executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3098                         TRACE("executedMenuId %d\n", executedMenuId);
3099
3100                         /* End the loop if executedMenuId is an item ID */
3101                         /* or if the job was done (executedMenuId = 0). */
3102                         fEndMenu = fRemove = (executedMenuId != -1);
3103                     }
3104                     /* No menu was selected by the mouse */
3105                     /* if the function was called by TrackPopupMenu, continue
3106                        with the menu tracking. If not, stop it */
3107                     else
3108                         fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3109
3110                     break;
3111
3112                 case WM_MOUSEMOVE:
3113                     /* the selected menu item must be changed every time */
3114                      /* the mouse moves. */
3115
3116                     if (hmenu)
3117                         fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3118
3119             } /* switch(msg.message) - mouse */
3120         }
3121         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3122         {
3123             fRemove = TRUE;  /* Keyboard messages are always removed */
3124             switch(msg.message)
3125             {
3126             case WM_KEYDOWN:
3127             case WM_SYSKEYDOWN:
3128                 switch(msg.wParam)
3129                 {
3130                 case VK_MENU:
3131                     fEndMenu = TRUE;
3132                     break;
3133
3134                 case VK_HOME:
3135                 case VK_END:
3136                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3137                                      NO_SELECTED_ITEM, FALSE, 0 );
3138                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3139                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3140                     break;
3141
3142                 case VK_UP:
3143                 case VK_DOWN: /* If on menu bar, pull-down the menu */
3144
3145                     menu = MENU_GetMenu( mt.hCurrentMenu );
3146                     if (!(menu->wFlags & MF_POPUP))
3147                         mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3148                     else      /* otherwise try to move selection */
3149                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
3150                                        (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3151                     break;
3152
3153                 case VK_LEFT:
3154                     MENU_KeyLeft( &mt, wFlags );
3155                     break;
3156
3157                 case VK_RIGHT:
3158                     MENU_KeyRight( &mt, wFlags );
3159                     break;
3160
3161                 case VK_ESCAPE:
3162                     fEndMenu = MENU_KeyEscape(&mt, wFlags);
3163                     break;
3164
3165                 case VK_F1:
3166                     {
3167                         HELPINFO hi;
3168                         hi.cbSize = sizeof(HELPINFO);
3169                         hi.iContextType = HELPINFO_MENUITEM;
3170                         if (menu->FocusedItem == NO_SELECTED_ITEM)
3171                             hi.iCtrlId = 0;
3172                         else
3173                             hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3174                         hi.hItemHandle = hmenu;
3175                         hi.dwContextId = menu->dwContextHelpID;
3176                         hi.MousePos = msg.pt;
3177                         SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3178                         break;
3179                     }
3180
3181                 default:
3182                     break;
3183                 }
3184                 break;  /* WM_KEYDOWN */
3185
3186             case WM_CHAR:
3187             case WM_SYSCHAR:
3188                 {
3189                     UINT        pos;
3190
3191                     if (msg.wParam == '\r' || msg.wParam == ' ')
3192                     {
3193                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3194                         fEndMenu = (executedMenuId != -2);
3195
3196                         break;
3197                     }
3198
3199                       /* Hack to avoid control chars. */
3200                       /* We will find a better way real soon... */
3201                     if (msg.wParam < 32) break;
3202
3203                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3204                                               LOWORD(msg.wParam), FALSE );
3205                     if (pos == (UINT)-2) fEndMenu = TRUE;
3206                     else if (pos == (UINT)-1) MessageBeep(0);
3207                     else
3208                     {
3209                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3210                                 TRUE, 0 );
3211                         executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3212                         fEndMenu = (executedMenuId != -2);
3213                     }
3214                 }
3215                 break;
3216             }  /* switch(msg.message) - kbd */
3217         }
3218         else
3219         {
3220             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3221             DispatchMessageW( &msg );
3222             continue;
3223         }
3224
3225         if (!fEndMenu) fRemove = TRUE;
3226
3227         /* finally remove message from the queue */
3228
3229         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3230             PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3231         else mt.trackFlags &= ~TF_SKIPREMOVE;
3232     }
3233
3234     set_capture_window( 0, GUI_INMENUMODE, NULL );
3235
3236     /* If dropdown is still painted and the close box is clicked on
3237        then the menu will be destroyed as part of the DispatchMessage above.
3238        This will then invalidate the menu handle in mt.hTopMenu. We should
3239        check for this first.  */
3240     if( IsMenu( mt.hTopMenu ) )
3241     {
3242         menu = MENU_GetMenu( mt.hTopMenu );
3243
3244         if( IsWindow( mt.hOwnerWnd ) )
3245         {
3246             MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3247
3248             if (menu && (menu->wFlags & MF_POPUP))
3249             {
3250                 DestroyWindow( menu->hWnd );
3251                 menu->hWnd = 0;
3252
3253                 if (!(wFlags & TPM_NONOTIFY))
3254                    SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3255                                  MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3256             }
3257             MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3258             SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3259         }
3260
3261         /* Reset the variable for hiding menu */
3262         if( menu ) menu->bTimeToHide = FALSE;
3263     }
3264
3265     /* The return value is only used by TrackPopupMenu */
3266     if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3267     if (executedMenuId == -1) executedMenuId = 0;
3268     return executedMenuId;
3269 }
3270
3271 /***********************************************************************
3272  *           MENU_InitTracking
3273  */
3274 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3275 {
3276     POPUPMENU *menu;
3277     
3278     TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3279
3280     HideCaret(0);
3281
3282     /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3283     if (!(wFlags & TPM_NONOTIFY))
3284        SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3285
3286     SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3287
3288     if (!(wFlags & TPM_NONOTIFY))
3289     {
3290        SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3291        /* If an app changed/recreated menu bar entries in WM_INITMENU
3292         * menu sizes will be recalculated once the menu created/shown.
3293         */
3294     }
3295     
3296     /* This makes the menus of applications built with Delphi work.
3297      * It also enables menus to be displayed in more than one window,
3298      * but there are some bugs left that need to be fixed in this case.
3299      */
3300     if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3301     
3302     return TRUE;
3303 }
3304 /***********************************************************************
3305  *           MENU_ExitTracking
3306  */
3307 static BOOL MENU_ExitTracking(HWND hWnd)
3308 {
3309     TRACE("hwnd=%p\n", hWnd);
3310
3311     SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3312     ShowCaret(0);
3313     top_popup = 0;
3314     return TRUE;
3315 }
3316
3317 /***********************************************************************
3318  *           MENU_TrackMouseMenuBar
3319  *
3320  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3321  */
3322 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3323 {
3324     HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3325     UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3326
3327     TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3328
3329     if (IsMenu(hMenu))
3330     {
3331         MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3332         MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3333         MENU_ExitTracking(hWnd);
3334     }
3335 }
3336
3337
3338 /***********************************************************************
3339  *           MENU_TrackKbdMenuBar
3340  *
3341  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3342  */
3343 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3344 {
3345     UINT uItem = NO_SELECTED_ITEM;
3346     HMENU hTrackMenu;
3347     UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3348
3349     TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3350
3351     /* find window that has a menu */
3352
3353     while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3354         if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3355
3356     /* check if we have to track a system menu */
3357
3358     hTrackMenu = GetMenu( hwnd );
3359     if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3360     {
3361         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3362         hTrackMenu = get_win_sys_menu( hwnd );
3363         uItem = 0;
3364         wParam |= HTSYSMENU; /* prevent item lookup */
3365     }
3366
3367     if (!IsMenu( hTrackMenu )) return;
3368
3369     MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3370
3371     if( wChar && wChar != ' ' )
3372     {
3373         uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3374         if ( uItem >= (UINT)(-2) )
3375         {
3376             if( uItem == (UINT)(-1) ) MessageBeep(0);
3377             /* schedule end of menu tracking */
3378             wFlags |= TF_ENDMENU;
3379             goto track_menu;
3380         }
3381     }
3382
3383     MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3384
3385     if (!(wParam & HTSYSMENU) || wChar == ' ')
3386     {
3387         if( uItem == NO_SELECTED_ITEM )
3388             MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3389         else
3390             PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3391     }
3392
3393 track_menu:
3394     MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3395     MENU_ExitTracking( hwnd );
3396 }
3397
3398
3399 /**********************************************************************
3400  *           TrackPopupMenu   (USER32.@)
3401  *
3402  * Like the win32 API, the function return the command ID only if the
3403  * flag TPM_RETURNCMD is on.
3404  *
3405  */
3406 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3407                            INT nReserved, HWND hWnd, const RECT *lpRect )
3408 {
3409     BOOL ret = FALSE;
3410
3411     TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3412            hMenu, wFlags, x, y, nReserved, hWnd, wine_dbgstr_rect(lpRect));
3413
3414     MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3415
3416     /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3417     if (!(wFlags & TPM_NONOTIFY))
3418         SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3419
3420     if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3421         ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3422     MENU_ExitTracking(hWnd);
3423
3424     return ret;
3425 }
3426
3427 /**********************************************************************
3428  *           TrackPopupMenuEx   (USER32.@)
3429  */
3430 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3431                                 HWND hWnd, LPTPMPARAMS lpTpm )
3432 {
3433     FIXME("not fully implemented\n" );
3434     return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3435                              lpTpm ? &lpTpm->rcExclude : NULL );
3436 }
3437
3438 /***********************************************************************
3439  *           PopupMenuWndProc
3440  *
3441  * NOTE: Windows has totally different (and undocumented) popup wndproc.
3442  */
3443 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3444 {
3445     TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3446
3447     switch(message)
3448     {
3449     case WM_CREATE:
3450         {
3451             CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3452             SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3453             return 0;
3454         }
3455
3456     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3457         return MA_NOACTIVATE;
3458
3459     case WM_PAINT:
3460         {
3461             PAINTSTRUCT ps;
3462             BeginPaint( hwnd, &ps );
3463             MENU_DrawPopupMenu( hwnd, ps.hdc,
3464                                 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3465             EndPaint( hwnd, &ps );
3466             return 0;
3467         }
3468     case WM_ERASEBKGND:
3469         return 1;
3470
3471     case WM_DESTROY:
3472         /* zero out global pointer in case resident popup window was destroyed. */
3473         if (hwnd == top_popup) top_popup = 0;
3474         break;
3475
3476     case WM_SHOWWINDOW:
3477
3478         if( wParam )
3479         {
3480             if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3481         }
3482         else
3483             SetWindowLongPtrW( hwnd, 0, 0 );
3484         break;
3485
3486     case MM_SETMENUHANDLE:
3487         SetWindowLongPtrW( hwnd, 0, wParam );
3488         break;
3489
3490     case MM_GETMENUHANDLE:
3491         return GetWindowLongPtrW( hwnd, 0 );
3492
3493     default:
3494         return DefWindowProcW( hwnd, message, wParam, lParam );
3495     }
3496     return 0;
3497 }
3498
3499
3500 /***********************************************************************
3501  *           MENU_GetMenuBarHeight
3502  *
3503  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3504  */
3505 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3506                               INT orgX, INT orgY )
3507 {
3508     HDC hdc;
3509     RECT rectBar;
3510     LPPOPUPMENU lppop;
3511
3512     TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3513
3514     if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3515
3516     hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3517     SelectObject( hdc, get_menu_font(FALSE));
3518     SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3519     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3520     ReleaseDC( hwnd, hdc );
3521     return lppop->Height;
3522 }
3523
3524
3525 /*******************************************************************
3526  *         ChangeMenuA    (USER32.@)
3527  */
3528 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3529                              UINT id, UINT flags )
3530 {
3531     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3532     if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3533                                                  id, data );
3534     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3535     if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3536                                                 id, data );
3537     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3538                                               flags & MF_BYPOSITION ? pos : id,
3539                                               flags & ~MF_REMOVE );
3540     /* Default: MF_INSERT */
3541     return InsertMenuA( hMenu, pos, flags, id, data );
3542 }
3543
3544
3545 /*******************************************************************
3546  *         ChangeMenuW    (USER32.@)
3547  */
3548 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3549                              UINT id, UINT flags )
3550 {
3551     TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3552     if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3553                                                  id, data );
3554     if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3555     if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3556                                                 id, data );
3557     if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3558                                               flags & MF_BYPOSITION ? pos : id,
3559                                               flags & ~MF_REMOVE );
3560     /* Default: MF_INSERT */
3561     return InsertMenuW( hMenu, pos, flags, id, data );
3562 }
3563
3564
3565 /*******************************************************************
3566  *         CheckMenuItem    (USER32.@)
3567  */
3568 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3569 {
3570     MENUITEM *item;
3571     DWORD ret;
3572
3573     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3574     ret = item->fState & MF_CHECKED;
3575     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3576     else item->fState &= ~MF_CHECKED;
3577     return ret;
3578 }
3579
3580
3581 /**********************************************************************
3582  *         EnableMenuItem    (USER32.@)
3583  */
3584 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3585 {
3586     UINT    oldflags;
3587     MENUITEM *item;
3588     POPUPMENU *menu;
3589
3590     TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3591
3592     /* Get the Popupmenu to access the owner menu */
3593     if (!(menu = MENU_GetMenu(hMenu)))
3594         return (UINT)-1;
3595
3596     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3597         return (UINT)-1;
3598
3599     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3600     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3601
3602     /* If the close item in the system menu change update the close button */
3603     if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3604     {
3605         if (menu->hSysMenuOwner != 0)
3606         {
3607             RECT rc;
3608             POPUPMENU* parentMenu;
3609
3610             /* Get the parent menu to access*/
3611             if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3612                 return (UINT)-1;
3613
3614             /* Refresh the frame to reflect the change */
3615             GetWindowRect(parentMenu->hWnd, &rc);
3616             MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3617             rc.bottom = 0;
3618             RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3619         }
3620     }
3621
3622     return oldflags;
3623 }
3624
3625
3626 /*******************************************************************
3627  *         GetMenuStringA    (USER32.@)
3628  */
3629 INT WINAPI GetMenuStringA(
3630         HMENU hMenu,    /* [in] menuhandle */
3631         UINT wItemID,   /* [in] menu item (dep. on wFlags) */
3632         LPSTR str,      /* [out] outbuffer. If NULL, func returns entry length*/
3633         INT nMaxSiz,    /* [in] length of buffer. if 0, func returns entry len*/
3634         UINT wFlags     /* [in] MF_ flags */
3635 ) {
3636     MENUITEM *item;
3637
3638     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3639     if (str && nMaxSiz) str[0] = '\0';
3640     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3641         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3642         return 0;
3643     }
3644     if (!item->text) return 0;
3645     if (!str || !nMaxSiz) return strlenW(item->text);
3646     if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3647         str[nMaxSiz-1] = 0;
3648     TRACE("returning %s\n", debugstr_a(str));
3649     return strlen(str);
3650 }
3651
3652
3653 /*******************************************************************
3654  *         GetMenuStringW    (USER32.@)
3655  */
3656 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3657                                LPWSTR str, INT nMaxSiz, UINT wFlags )
3658 {
3659     MENUITEM *item;
3660
3661     TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3662     if (str && nMaxSiz) str[0] = '\0';
3663     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3664         SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3665         return 0;
3666     }
3667     if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3668     if( !(item->text)) {
3669         str[0] = 0;
3670         return 0;
3671     }
3672     lstrcpynW( str, item->text, nMaxSiz );
3673     TRACE("returning %s\n", debugstr_w(str));
3674     return strlenW(str);
3675 }
3676
3677
3678 /**********************************************************************
3679  *         HiliteMenuItem    (USER32.@)
3680  */
3681 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3682                                 UINT wHilite )
3683 {
3684     LPPOPUPMENU menu;
3685     TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3686     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3687     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3688     if (menu->FocusedItem == wItemID) return TRUE;
3689     MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3690     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3691     return TRUE;
3692 }
3693
3694
3695 /**********************************************************************
3696  *         GetMenuState    (USER32.@)
3697  */
3698 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3699 {
3700     MENUITEM *item;
3701     TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3702     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3703     debug_print_menuitem ("  item: ", item, "");
3704     if (item->fType & MF_POPUP)
3705     {
3706         POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3707         if (!menu) return -1;
3708         else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3709     }
3710     else
3711     {
3712         /* We used to (from way back then) mask the result to 0xff.  */
3713         /* I don't know why and it seems wrong as the documented */
3714         /* return flag MF_SEPARATOR is outside that mask.  */
3715         return (item->fType | item->fState);
3716     }
3717 }
3718
3719
3720 /**********************************************************************
3721  *         GetMenuItemCount    (USER32.@)
3722  */
3723 INT WINAPI GetMenuItemCount( HMENU hMenu )
3724 {
3725     LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3726     if (!menu) return -1;
3727     TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3728     return menu->nItems;
3729 }
3730
3731
3732 /**********************************************************************
3733  *         GetMenuItemID    (USER32.@)
3734  */
3735 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3736 {
3737     MENUITEM * lpmi;
3738
3739     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3740     if (lpmi->fType & MF_POPUP) return -1;
3741     return lpmi->wID;
3742
3743 }
3744
3745
3746 /*******************************************************************
3747  *         InsertMenuW    (USER32.@)
3748  */
3749 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3750                          UINT_PTR id, LPCWSTR str )
3751 {
3752     MENUITEM *item;
3753
3754     if (IS_STRING_ITEM(flags) && str)
3755         TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3756               hMenu, pos, flags, id, debugstr_w(str) );
3757     else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3758                hMenu, pos, flags, id, str );
3759
3760     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3761
3762     if (!(MENU_SetItemData( item, flags, id, str )))
3763     {
3764         RemoveMenu( hMenu, pos, flags );
3765         return FALSE;
3766     }
3767
3768     item->hCheckBit = item->hUnCheckBit = 0;
3769     return TRUE;
3770 }
3771
3772
3773 /*******************************************************************
3774  *         InsertMenuA    (USER32.@)
3775  */
3776 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3777                          UINT_PTR id, LPCSTR str )
3778 {
3779     BOOL ret = FALSE;
3780
3781     if (IS_STRING_ITEM(flags) && str)
3782     {
3783         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3784         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3785         if (newstr)
3786         {
3787             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3788             ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3789             HeapFree( GetProcessHeap(), 0, newstr );
3790         }
3791         return ret;
3792     }
3793     else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3794 }
3795
3796
3797 /*******************************************************************
3798  *         AppendMenuA    (USER32.@)
3799  */
3800 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3801                          UINT_PTR id, LPCSTR data )
3802 {
3803     return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3804 }
3805
3806
3807 /*******************************************************************
3808  *         AppendMenuW    (USER32.@)
3809  */
3810 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3811                          UINT_PTR id, LPCWSTR data )
3812 {
3813     return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3814 }
3815
3816
3817 /**********************************************************************
3818  *         RemoveMenu    (USER32.@)
3819  */
3820 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3821 {
3822     LPPOPUPMENU menu;
3823     MENUITEM *item;
3824
3825     TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3826     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3827     if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3828
3829       /* Remove item */
3830
3831     MENU_FreeItemData( item );
3832
3833     if (--menu->nItems == 0)
3834     {
3835         HeapFree( GetProcessHeap(), 0, menu->items );
3836         menu->items = NULL;
3837     }
3838     else
3839     {
3840         while(nPos < menu->nItems)
3841         {
3842             *item = *(item+1);
3843             item++;
3844             nPos++;
3845         }
3846         menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3847                                    menu->nItems * sizeof(MENUITEM) );
3848     }
3849     return TRUE;
3850 }
3851
3852
3853 /**********************************************************************
3854  *         DeleteMenu    (USER32.@)
3855  */
3856 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3857 {
3858     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3859     if (!item) return FALSE;
3860     if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3861       /* nPos is now the position of the item */
3862     RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3863     return TRUE;
3864 }
3865
3866
3867 /*******************************************************************
3868  *         ModifyMenuW    (USER32.@)
3869  */
3870 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3871                          UINT_PTR id, LPCWSTR str )
3872 {
3873     MENUITEM *item;
3874
3875     if (IS_STRING_ITEM(flags))
3876         TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3877     else
3878         TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3879
3880     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3881     MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3882     return MENU_SetItemData( item, flags, id, str );
3883 }
3884
3885
3886 /*******************************************************************
3887  *         ModifyMenuA    (USER32.@)
3888  */
3889 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3890                          UINT_PTR id, LPCSTR str )
3891 {
3892     BOOL ret = FALSE;
3893
3894     if (IS_STRING_ITEM(flags) && str)
3895     {
3896         INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3897         LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3898         if (newstr)
3899         {
3900             MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3901             ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3902             HeapFree( GetProcessHeap(), 0, newstr );
3903         }
3904         return ret;
3905     }
3906     else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3907 }
3908
3909
3910 /**********************************************************************
3911  *         CreatePopupMenu    (USER32.@)
3912  */
3913 HMENU WINAPI CreatePopupMenu(void)
3914 {
3915     HMENU hmenu;
3916     POPUPMENU *menu;
3917
3918     if (!(hmenu = CreateMenu())) return 0;
3919     menu = MENU_GetMenu( hmenu );
3920     menu->wFlags |= MF_POPUP;
3921     menu->bTimeToHide = FALSE;
3922     return hmenu;
3923 }
3924
3925
3926 /**********************************************************************
3927  *         GetMenuCheckMarkDimensions    (USER.417)
3928  *         GetMenuCheckMarkDimensions    (USER32.@)
3929  */
3930 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3931 {
3932     return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3933 }
3934
3935
3936 /**********************************************************************
3937  *         SetMenuItemBitmaps    (USER32.@)
3938  */
3939 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3940                                     HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3941 {
3942     MENUITEM *item;
3943
3944     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3945
3946     if (!hNewCheck && !hNewUnCheck)
3947     {
3948         item->fState &= ~MF_USECHECKBITMAPS;
3949     }
3950     else  /* Install new bitmaps */
3951     {
3952         item->hCheckBit = hNewCheck;
3953         item->hUnCheckBit = hNewUnCheck;
3954         item->fState |= MF_USECHECKBITMAPS;
3955     }
3956     return TRUE;
3957 }
3958
3959
3960 /**********************************************************************
3961  *         CreateMenu    (USER32.@)
3962  */
3963 HMENU WINAPI CreateMenu(void)
3964 {
3965     HMENU hMenu;
3966     LPPOPUPMENU menu;
3967     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3968     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3969
3970     ZeroMemory(menu, sizeof(POPUPMENU));
3971     menu->wMagic = MENU_MAGIC;
3972     menu->FocusedItem = NO_SELECTED_ITEM;
3973     menu->bTimeToHide = FALSE;
3974
3975     TRACE("return %p\n", hMenu );
3976
3977     return hMenu;
3978 }
3979
3980
3981 /**********************************************************************
3982  *         DestroyMenu    (USER32.@)
3983  */
3984 BOOL WINAPI DestroyMenu( HMENU hMenu )
3985 {
3986     LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3987
3988     TRACE("(%p)\n", hMenu);
3989
3990
3991     if (!lppop) return FALSE;
3992
3993     lppop->wMagic = 0;  /* Mark it as destroyed */
3994
3995     /* DestroyMenu should not destroy system menu popup owner */
3996     if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3997     {
3998         DestroyWindow( lppop->hWnd );
3999         lppop->hWnd = 0;
4000     }
4001
4002     if (lppop->items) /* recursively destroy submenus */
4003     {
4004         int i;
4005         MENUITEM *item = lppop->items;
4006         for (i = lppop->nItems; i > 0; i--, item++)
4007         {
4008             if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4009             MENU_FreeItemData( item );
4010         }
4011         HeapFree( GetProcessHeap(), 0, lppop->items );
4012     }
4013     USER_HEAP_FREE( hMenu );
4014     return TRUE;
4015 }
4016
4017
4018 /**********************************************************************
4019  *         GetSystemMenu    (USER32.@)
4020  */
4021 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4022 {
4023     WND *wndPtr = WIN_GetPtr( hWnd );
4024     HMENU retvalue = 0;
4025
4026     if (wndPtr == WND_DESKTOP) return 0;
4027     if (wndPtr == WND_OTHER_PROCESS)
4028     {
4029         if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4030     }
4031     else if (wndPtr)
4032     {
4033         if (wndPtr->hSysMenu && bRevert)
4034         {
4035             DestroyMenu(wndPtr->hSysMenu);
4036             wndPtr->hSysMenu = 0;
4037         }
4038
4039         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4040             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4041
4042         if( wndPtr->hSysMenu )
4043         {
4044             POPUPMENU *menu;
4045             retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4046
4047             /* Store the dummy sysmenu handle to facilitate the refresh */
4048             /* of the close button if the SC_CLOSE item change */
4049             menu = MENU_GetMenu(retvalue);
4050             if ( menu )
4051                menu->hSysMenuOwner = wndPtr->hSysMenu;
4052         }
4053         WIN_ReleasePtr( wndPtr );
4054     }
4055     return bRevert ? 0 : retvalue;
4056 }
4057
4058
4059 /*******************************************************************
4060  *         SetSystemMenu    (USER32.@)
4061  */
4062 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4063 {
4064     WND *wndPtr = WIN_GetPtr( hwnd );
4065
4066     if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4067     {
4068         if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4069         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4070         WIN_ReleasePtr( wndPtr );
4071         return TRUE;
4072     }
4073     return FALSE;
4074 }
4075
4076
4077 /**********************************************************************
4078  *         GetMenu    (USER32.@)
4079  */
4080 HMENU WINAPI GetMenu( HWND hWnd )
4081 {
4082     HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4083     TRACE("for %p returning %p\n", hWnd, retvalue);
4084     return retvalue;
4085 }
4086
4087 /**********************************************************************
4088  *         GetMenuBarInfo    (USER32.@)
4089  */
4090 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4091 {
4092     FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4093     return FALSE;
4094 }
4095
4096 /**********************************************************************
4097  *         MENU_SetMenu
4098  *
4099  * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4100  * SetWindowPos call that would result if SetMenu were called directly.
4101  */
4102 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4103 {
4104     TRACE("(%p, %p);\n", hWnd, hMenu);
4105
4106     if (hMenu && !IsMenu(hMenu))
4107     {
4108         WARN("hMenu %p is not a menu handle\n", hMenu);
4109         return FALSE;
4110     }
4111     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4112         return FALSE;
4113
4114     hWnd = WIN_GetFullHandle( hWnd );
4115     if (GetCapture() == hWnd)
4116         set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
4117
4118     if (hMenu != 0)
4119     {
4120         LPPOPUPMENU lpmenu;
4121
4122         if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4123
4124         lpmenu->hWnd = hWnd;
4125         lpmenu->Height = 0;  /* Make sure we recalculate the size */
4126     }
4127     SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4128     return TRUE;
4129 }
4130
4131
4132 /**********************************************************************
4133  *         SetMenu    (USER32.@)
4134  */
4135 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4136 {   
4137     if(!MENU_SetMenu(hWnd, hMenu))
4138         return FALSE;
4139  
4140     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4141                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4142     return TRUE;
4143 }
4144
4145
4146 /**********************************************************************
4147  *         GetSubMenu    (USER32.@)
4148  */
4149 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4150 {
4151     MENUITEM * lpmi;
4152
4153     if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4154     if (!(lpmi->fType & MF_POPUP)) return 0;
4155     return lpmi->hSubMenu;
4156 }
4157
4158
4159 /**********************************************************************
4160  *         DrawMenuBar    (USER32.@)
4161  */
4162 BOOL WINAPI DrawMenuBar( HWND hWnd )
4163 {
4164     LPPOPUPMENU lppop;
4165     HMENU hMenu = GetMenu(hWnd);
4166
4167     if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4168         return FALSE;
4169     if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4170
4171     lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4172     lppop->hwndOwner = hWnd;
4173     SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4174                   SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4175     return TRUE;
4176 }
4177
4178 /***********************************************************************
4179  *           DrawMenuBarTemp   (USER32.@)
4180  *
4181  * UNDOCUMENTED !!
4182  *
4183  * called by W98SE desk.cpl Control Panel Applet
4184  *
4185  * Not 100% sure about the param names, but close.
4186  */
4187 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4188 {
4189     LPPOPUPMENU lppop;
4190     UINT i,retvalue;
4191     HFONT hfontOld = 0;
4192     BOOL flat_menu = FALSE;
4193
4194     SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4195
4196     if (!hMenu)
4197         hMenu = GetMenu(hwnd);
4198
4199     if (!hFont)
4200         hFont = get_menu_font(FALSE);
4201
4202     lppop = MENU_GetMenu( hMenu );
4203     if (lppop == NULL || lprect == NULL)
4204     {
4205         retvalue = GetSystemMetrics(SM_CYMENU);
4206         goto END;
4207     }
4208
4209     TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4210
4211     hfontOld = SelectObject( hDC, hFont);
4212
4213     if (lppop->Height == 0)
4214         MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4215
4216     lprect->bottom = lprect->top + lppop->Height;
4217
4218     FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4219
4220     SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4221     MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4222     LineTo( hDC, lprect->right, lprect->bottom );
4223
4224     if (lppop->nItems == 0)
4225     {
4226         retvalue = GetSystemMetrics(SM_CYMENU);
4227         goto END;
4228     }
4229
4230     for (i = 0; i < lppop->nItems; i++)
4231     {
4232         MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4233                            hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4234     }
4235     retvalue = lppop->Height;
4236
4237 END:
4238     if (hfontOld) SelectObject (hDC, hfontOld);
4239     return retvalue;
4240 }
4241
4242 /***********************************************************************
4243  *           EndMenu   (USER.187)
4244  *           EndMenu   (USER32.@)
4245  */
4246 BOOL WINAPI EndMenu(void)
4247 {
4248     /* if we are in the menu code, and it is active */
4249     if (!fEndMenu && top_popup)
4250     {
4251         /* terminate the menu handling code */
4252         fEndMenu = TRUE;
4253
4254         /* needs to be posted to wakeup the internal menu handler */
4255         /* which will now terminate the menu, in the event that */
4256         /* the main window was minimized, or lost focus, so we */
4257         /* don't end up with an orphaned menu */
4258         PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4259     }
4260     return fEndMenu;
4261 }
4262
4263
4264 /***********************************************************************
4265  *           LookupMenuHandle   (USER.217)
4266  */
4267 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4268 {
4269     HMENU hmenu32 = HMENU_32(hmenu);
4270     UINT id32 = id;
4271     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4272     else return HMENU_16(hmenu32);
4273 }
4274
4275
4276 /**********************************************************************
4277  *          LoadMenu    (USER.150)
4278  */
4279 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4280 {
4281     HRSRC16 hRsrc;
4282     HGLOBAL16 handle;
4283     HMENU16 hMenu;
4284
4285     if (HIWORD(name) && name[0] == '#') name = ULongToPtr(atoi( name + 1 ));
4286     if (!name) return 0;
4287
4288     instance = GetExePtr( instance );
4289     if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4290     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4291     hMenu = LoadMenuIndirect16(LockResource16(handle));
4292     FreeResource16( handle );
4293     return hMenu;
4294 }
4295
4296
4297 /*****************************************************************
4298  *        LoadMenuA   (USER32.@)
4299  */
4300 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4301 {
4302     HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4303     if (!hrsrc) return 0;
4304     return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4305 }
4306
4307
4308 /*****************************************************************
4309  *        LoadMenuW   (USER32.@)
4310  */
4311 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4312 {
4313     HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4314     if (!hrsrc) return 0;
4315     return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4316 }
4317
4318
4319 /**********************************************************************
4320  *          LoadMenuIndirect    (USER.220)
4321  */
4322 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4323 {
4324     HMENU hMenu;
4325     WORD version, offset;
4326     LPCSTR p = (LPCSTR)template;
4327
4328     TRACE("(%p)\n", template );
4329     version = GET_WORD(p);
4330     p += sizeof(WORD);
4331     if (version)
4332     {
4333         WARN("version must be 0 for Win16\n" );
4334         return 0;
4335     }
4336     offset = GET_WORD(p);
4337     p += sizeof(WORD) + offset;
4338     if (!(hMenu = CreateMenu())) return 0;
4339     if (!MENU_ParseResource( p, hMenu, FALSE ))
4340     {
4341         DestroyMenu( hMenu );
4342         return 0;
4343     }
4344     return HMENU_16(hMenu);
4345 }
4346
4347
4348 /**********************************************************************
4349  *          LoadMenuIndirectW    (USER32.@)
4350  */
4351 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4352 {
4353     HMENU hMenu;
4354     WORD version, offset;
4355     LPCSTR p = (LPCSTR)template;
4356
4357     version = GET_WORD(p);
4358     p += sizeof(WORD);
4359     TRACE("%p, ver %d\n", template, version );
4360     switch (version)
4361     {
4362       case 0: /* standard format is version of 0 */
4363         offset = GET_WORD(p);
4364         p += sizeof(WORD) + offset;
4365         if (!(hMenu = CreateMenu())) return 0;
4366         if (!MENU_ParseResource( p, hMenu, TRUE ))
4367           {
4368             DestroyMenu( hMenu );
4369             return 0;
4370           }
4371         return hMenu;
4372       case 1: /* extended format is version of 1 */
4373         offset = GET_WORD(p);
4374         p += sizeof(WORD) + offset;
4375         if (!(hMenu = CreateMenu())) return 0;
4376         if (!MENUEX_ParseResource( p, hMenu))
4377           {
4378             DestroyMenu( hMenu );
4379             return 0;
4380           }
4381         return hMenu;
4382       default:
4383         ERR("version %d not supported.\n", version);
4384         return 0;
4385     }
4386 }
4387
4388
4389 /**********************************************************************
4390  *          LoadMenuIndirectA    (USER32.@)
4391  */
4392 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4393 {
4394     return LoadMenuIndirectW( template );
4395 }
4396
4397
4398 /**********************************************************************
4399  *              IsMenu    (USER32.@)
4400  */
4401 BOOL WINAPI IsMenu(HMENU hmenu)
4402 {
4403     LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4404
4405     if (!menu)
4406     {
4407         SetLastError(ERROR_INVALID_MENU_HANDLE);
4408         return FALSE;
4409     }
4410     return TRUE;
4411 }
4412
4413 /**********************************************************************
4414  *              GetMenuItemInfo_common
4415  */
4416
4417 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4418                                         LPMENUITEMINFOW lpmii, BOOL unicode)
4419 {
4420     MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4421
4422     debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4423
4424     if (!menu)
4425         return FALSE;
4426     
4427     if( lpmii->fMask & MIIM_TYPE) {
4428         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4429             WARN("invalid combination of fMask bits used\n");
4430             /* this does not happen on Win9x/ME */
4431             SetLastError( ERROR_INVALID_PARAMETER);
4432             return FALSE;
4433         }
4434         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4435         if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4436         lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4437         if( lpmii->fType & MFT_BITMAP) {
4438             lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4439             lpmii->cch = 0;
4440         } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4441             /* this does not happen on Win9x/ME */
4442             lpmii->dwTypeData = 0;
4443             lpmii->cch = 0;
4444         }
4445     }
4446
4447     /* copy the text string */
4448     if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4449          if( !menu->text ) {
4450                 if(lpmii->dwTypeData && lpmii->cch) {
4451                     lpmii->cch = 0;
4452                     if( unicode)
4453                         *((WCHAR *)lpmii->dwTypeData) = 0;
4454                     else
4455                         *((CHAR *)lpmii->dwTypeData) = 0;
4456                 }
4457          } else {
4458             int len;
4459             if (unicode)
4460             {
4461                 len = strlenW(menu->text);
4462                 if(lpmii->dwTypeData && lpmii->cch)
4463                     lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4464             }
4465             else
4466             {
4467                 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4468                         0, NULL, NULL ) - 1;
4469                 if(lpmii->dwTypeData && lpmii->cch)
4470                     if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4471                             (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4472                         ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4473             }
4474             /* if we've copied a substring we return its length */
4475             if(lpmii->dwTypeData && lpmii->cch)
4476                 if (lpmii->cch <= len + 1)
4477                     lpmii->cch--;
4478                 else
4479                     lpmii->cch = len;
4480             else {
4481                 /* return length of string */
4482                 /* not on Win9x/ME if fType & MFT_BITMAP */
4483                 lpmii->cch = len;
4484             }
4485         }
4486     }
4487
4488     if (lpmii->fMask & MIIM_FTYPE)
4489         lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4490
4491     if (lpmii->fMask & MIIM_BITMAP)
4492         lpmii->hbmpItem = menu->hbmpItem;
4493
4494     if (lpmii->fMask & MIIM_STATE)
4495         lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4496
4497     if (lpmii->fMask & MIIM_ID)
4498         lpmii->wID = menu->wID;
4499
4500     if (lpmii->fMask & MIIM_SUBMENU)
4501         lpmii->hSubMenu = menu->hSubMenu;
4502     else {
4503         /* hSubMenu is always cleared 
4504          * (not on Win9x/ME ) */
4505         lpmii->hSubMenu = 0;
4506     }
4507
4508     if (lpmii->fMask & MIIM_CHECKMARKS) {
4509         lpmii->hbmpChecked = menu->hCheckBit;
4510         lpmii->hbmpUnchecked = menu->hUnCheckBit;
4511     }
4512     if (lpmii->fMask & MIIM_DATA)
4513         lpmii->dwItemData = menu->dwItemData;
4514
4515   return TRUE;
4516 }
4517
4518 /**********************************************************************
4519  *              GetMenuItemInfoA    (USER32.@)
4520  */
4521 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4522                                   LPMENUITEMINFOA lpmii)
4523 {
4524     BOOL ret;
4525     MENUITEMINFOA mii;
4526     if( lpmii->cbSize != sizeof( mii) &&
4527             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4528         SetLastError( ERROR_INVALID_PARAMETER);
4529         return FALSE;
4530     }
4531     memcpy( &mii, lpmii, lpmii->cbSize);
4532     mii.cbSize = sizeof( mii);
4533     ret = GetMenuItemInfo_common (hmenu, item, bypos,
4534                                     (LPMENUITEMINFOW)&mii, FALSE);
4535     mii.cbSize = lpmii->cbSize;
4536     memcpy( lpmii, &mii, mii.cbSize);
4537     return ret;
4538 }
4539
4540 /**********************************************************************
4541  *              GetMenuItemInfoW    (USER32.@)
4542  */
4543 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4544                                   LPMENUITEMINFOW lpmii)
4545 {
4546     BOOL ret;
4547     MENUITEMINFOW mii;
4548     if( lpmii->cbSize != sizeof( mii) &&
4549             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4550         SetLastError( ERROR_INVALID_PARAMETER);
4551         return FALSE;
4552     }
4553     memcpy( &mii, lpmii, lpmii->cbSize);
4554     mii.cbSize = sizeof( mii);
4555     ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4556     mii.cbSize = lpmii->cbSize;
4557     memcpy( lpmii, &mii, mii.cbSize);
4558     return ret;
4559 }
4560
4561
4562 /* set a menu item text from a ASCII or Unicode string */
4563 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4564 {
4565     if (!text)
4566         menu->text = NULL;
4567     else if (unicode)
4568     {
4569         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4570             strcpyW( menu->text, text );
4571     }
4572     else
4573     {
4574         LPCSTR str = (LPCSTR)text;
4575         int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4576         if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4577             MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4578     }
4579 }
4580
4581
4582 /**********************************************************************
4583  *              SetMenuItemInfo_common
4584  */
4585
4586 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4587                                        const MENUITEMINFOW *lpmii,
4588                                        BOOL unicode)
4589 {
4590     if (!menu) return FALSE;
4591
4592     debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4593
4594     if (lpmii->fMask & MIIM_TYPE ) {
4595         if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4596             WARN("invalid combination of fMask bits used\n");
4597             /* this does not happen on Win9x/ME */
4598             SetLastError( ERROR_INVALID_PARAMETER);
4599             return FALSE;
4600         }
4601
4602         /* Remove the old type bits and replace them with the new ones */
4603         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4604         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4605
4606         if (IS_STRING_ITEM(menu->fType)) {
4607             HeapFree(GetProcessHeap(), 0, menu->text);
4608             set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4609         } else if( (menu->fType) & MFT_BITMAP)
4610                 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4611     }
4612
4613     if (lpmii->fMask & MIIM_FTYPE ) {
4614         if(( lpmii->fType & MFT_BITMAP)) {
4615             SetLastError( ERROR_INVALID_PARAMETER);
4616             return FALSE;
4617         }
4618         menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4619         menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4620     }
4621     if (lpmii->fMask & MIIM_STRING ) {
4622         /* free the string when used */
4623         HeapFree(GetProcessHeap(), 0, menu->text);
4624         set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4625     }
4626
4627     if (lpmii->fMask & MIIM_STATE)
4628     {
4629          /* Other menu items having MFS_DEFAULT are not converted
4630            to normal items */
4631          menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4632     }
4633
4634     if (lpmii->fMask & MIIM_ID)
4635         menu->wID = lpmii->wID;
4636
4637     if (lpmii->fMask & MIIM_SUBMENU) {
4638         menu->hSubMenu = lpmii->hSubMenu;
4639         if (menu->hSubMenu) {
4640             POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4641             if (subMenu) {
4642                 subMenu->wFlags |= MF_POPUP;
4643                 menu->fType |= MF_POPUP;
4644             }
4645             else {
4646                 SetLastError( ERROR_INVALID_PARAMETER);
4647                 return FALSE;
4648             }
4649         }
4650         else
4651             menu->fType &= ~MF_POPUP;
4652     }
4653
4654     if (lpmii->fMask & MIIM_CHECKMARKS)
4655     {
4656         menu->hCheckBit = lpmii->hbmpChecked;
4657         menu->hUnCheckBit = lpmii->hbmpUnchecked;
4658     }
4659     if (lpmii->fMask & MIIM_DATA)
4660         menu->dwItemData = lpmii->dwItemData;
4661
4662     if (lpmii->fMask & MIIM_BITMAP)
4663         menu->hbmpItem = lpmii->hbmpItem;
4664
4665     if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4666         menu->fType |= MFT_SEPARATOR;
4667
4668     debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4669     return TRUE;
4670 }
4671
4672 /**********************************************************************
4673  *              SetMenuItemInfoA    (USER32.@)
4674  */
4675 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4676                                  const MENUITEMINFOA *lpmii)
4677 {
4678     MENUITEMINFOA mii;
4679
4680     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4681
4682     if( lpmii->cbSize != sizeof( mii) &&
4683             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4684         SetLastError( ERROR_INVALID_PARAMETER);
4685         return FALSE;
4686     }
4687     memcpy( &mii, lpmii, lpmii->cbSize);
4688     if( lpmii->cbSize != sizeof( mii)) {
4689         mii.cbSize = sizeof( mii);
4690         mii.hbmpItem = NULL;
4691     }
4692     return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4693                                     (const MENUITEMINFOW *)&mii, FALSE);
4694 }
4695
4696 /**********************************************************************
4697  *              SetMenuItemInfoW    (USER32.@)
4698  */
4699 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4700                                  const MENUITEMINFOW *lpmii)
4701 {
4702     MENUITEMINFOW mii;
4703
4704     TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4705
4706     if( lpmii->cbSize != sizeof( mii) &&
4707             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4708         SetLastError( ERROR_INVALID_PARAMETER);
4709         return FALSE;
4710     }
4711     memcpy( &mii, lpmii, lpmii->cbSize);
4712     if( lpmii->cbSize != sizeof( mii)) {
4713         mii.cbSize = sizeof( mii);
4714         mii.hbmpItem = NULL;
4715     }
4716     return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4717                 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4718 }
4719
4720 /**********************************************************************
4721  *              SetMenuDefaultItem    (USER32.@)
4722  *
4723  */
4724 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4725 {
4726         UINT i;
4727         POPUPMENU *menu;
4728         MENUITEM *item;
4729
4730         TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4731
4732         if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4733
4734         /* reset all default-item flags */
4735         item = menu->items;
4736         for (i = 0; i < menu->nItems; i++, item++)
4737         {
4738             item->fState &= ~MFS_DEFAULT;
4739         }
4740
4741         /* no default item */
4742         if ( -1 == uItem)
4743         {
4744             return TRUE;
4745         }
4746
4747         item = menu->items;
4748         if ( bypos )
4749         {
4750             if ( uItem >= menu->nItems ) return FALSE;
4751             item[uItem].fState |= MFS_DEFAULT;
4752             return TRUE;
4753         }
4754         else
4755         {
4756             for (i = 0; i < menu->nItems; i++, item++)
4757             {
4758                 if (item->wID == uItem)
4759                 {
4760                      item->fState |= MFS_DEFAULT;
4761                      return TRUE;
4762                 }
4763             }
4764
4765         }
4766         return FALSE;
4767 }
4768
4769 /**********************************************************************
4770  *              GetMenuDefaultItem    (USER32.@)
4771  */
4772 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4773 {
4774         POPUPMENU *menu;
4775         MENUITEM * item;
4776         UINT i = 0;
4777
4778         TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4779
4780         if (!(menu = MENU_GetMenu(hmenu))) return -1;
4781
4782         /* find default item */
4783         item = menu->items;
4784
4785         /* empty menu */
4786         if (! item) return -1;
4787
4788         while ( !( item->fState & MFS_DEFAULT ) )
4789         {
4790             i++; item++;
4791             if  (i >= menu->nItems ) return -1;
4792         }
4793
4794         /* default: don't return disabled items */
4795         if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4796
4797         /* search rekursiv when needed */
4798         if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
4799         {
4800             UINT ret;
4801             ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4802             if ( -1 != ret ) return ret;
4803
4804             /* when item not found in submenu, return the popup item */
4805         }
4806         return ( bypos ) ? i : item->wID;
4807
4808 }
4809
4810
4811 /**********************************************************************
4812  *              InsertMenuItemA    (USER32.@)
4813  */
4814 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4815                                 const MENUITEMINFOA *lpmii)
4816 {
4817     MENUITEM *item;
4818     MENUITEMINFOA mii;
4819
4820     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4821
4822     if( lpmii->cbSize != sizeof( mii) &&
4823             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4824         SetLastError( ERROR_INVALID_PARAMETER);
4825         return FALSE;
4826     }
4827     memcpy( &mii, lpmii, lpmii->cbSize);
4828     if( lpmii->cbSize != sizeof( mii)) {
4829         mii.cbSize = sizeof( mii);
4830         mii.hbmpItem = NULL;
4831     }
4832
4833     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4834     return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4835 }
4836
4837
4838 /**********************************************************************
4839  *              InsertMenuItemW    (USER32.@)
4840  */
4841 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4842                                 const MENUITEMINFOW *lpmii)
4843 {
4844     MENUITEM *item;
4845     MENUITEMINFOW mii;
4846
4847     TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4848
4849     if( lpmii->cbSize != sizeof( mii) &&
4850             lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4851         SetLastError( ERROR_INVALID_PARAMETER);
4852         return FALSE;
4853     }
4854     memcpy( &mii, lpmii, lpmii->cbSize);
4855     if( lpmii->cbSize != sizeof( mii)) {
4856         mii.cbSize = sizeof( mii);
4857         mii.hbmpItem = NULL;
4858     }
4859
4860     item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4861     return SetMenuItemInfo_common(item, &mii, TRUE);
4862 }
4863
4864 /**********************************************************************
4865  *              CheckMenuRadioItem    (USER32.@)
4866  */
4867
4868 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4869                                    UINT first, UINT last, UINT check,
4870                                    UINT bypos)
4871 {
4872     BOOL done = FALSE;
4873     UINT i;
4874     MENUITEM *mi_first = NULL, *mi_check;
4875     HMENU m_first, m_check;
4876
4877     for (i = first; i <= last; i++)
4878     {
4879         UINT pos = i;
4880
4881         if (!mi_first)
4882         {
4883             m_first = hMenu;
4884             mi_first = MENU_FindItem(&m_first, &pos, bypos);
4885             if (!mi_first) continue;
4886             mi_check = mi_first;
4887             m_check = m_first;
4888         }
4889         else
4890         {
4891             m_check = hMenu;
4892             mi_check = MENU_FindItem(&m_check, &pos, bypos);
4893             if (!mi_check) continue;
4894         }
4895
4896         if (m_first != m_check) continue;
4897         if (mi_check->fType == MFT_SEPARATOR) continue;
4898
4899         if (i == check)
4900         {
4901             mi_check->fType |= MFT_RADIOCHECK;
4902             mi_check->fState |= MFS_CHECKED;
4903             done = TRUE;
4904         }
4905         else
4906         {
4907             /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4908             mi_check->fState &= ~MFS_CHECKED;
4909         }
4910     }
4911
4912     return done;
4913 }
4914
4915
4916 /**********************************************************************
4917  *              GetMenuItemRect    (USER32.@)
4918  *
4919  *      ATTENTION: Here, the returned values in rect are the screen
4920  *                 coordinates of the item just like if the menu was
4921  *                 always on the upper left side of the application.
4922  *
4923  */
4924 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4925                                  LPRECT rect)
4926 {
4927      POPUPMENU *itemMenu;
4928      MENUITEM *item;
4929      HWND referenceHwnd;
4930
4931      TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4932
4933      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4934      referenceHwnd = hwnd;
4935
4936      if(!hwnd)
4937      {
4938          itemMenu = MENU_GetMenu(hMenu);
4939          if (itemMenu == NULL)
4940              return FALSE;
4941
4942          if(itemMenu->hWnd == 0)
4943              return FALSE;
4944          referenceHwnd = itemMenu->hWnd;
4945      }
4946
4947      if ((rect == NULL) || (item == NULL))
4948          return FALSE;
4949
4950      *rect = item->rect;
4951
4952      MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4953
4954      return TRUE;
4955 }
4956
4957
4958 /**********************************************************************
4959  *              SetMenuInfo    (USER32.@)
4960  *
4961  * FIXME
4962  *      MIM_APPLYTOSUBMENUS
4963  *      actually use the items to draw the menu
4964  */
4965 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4966 {
4967     POPUPMENU *menu;
4968
4969     TRACE("(%p %p)\n", hMenu, lpmi);
4970
4971     if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4972     {
4973
4974         if (lpmi->fMask & MIM_BACKGROUND)
4975             menu->hbrBack = lpmi->hbrBack;
4976
4977         if (lpmi->fMask & MIM_HELPID)
4978             menu->dwContextHelpID = lpmi->dwContextHelpID;
4979
4980         if (lpmi->fMask & MIM_MAXHEIGHT)
4981             menu->cyMax = lpmi->cyMax;
4982
4983         if (lpmi->fMask & MIM_MENUDATA)
4984             menu->dwMenuData = lpmi->dwMenuData;
4985
4986         if (lpmi->fMask & MIM_STYLE)
4987         {
4988             menu->dwStyle = lpmi->dwStyle;
4989             if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4990             if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4991             if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4992         }
4993
4994         return TRUE;
4995     }
4996     return FALSE;
4997 }
4998
4999 /**********************************************************************
5000  *              GetMenuInfo    (USER32.@)
5001  *
5002  *  NOTES
5003  *      win98/NT5.0
5004  *
5005  */
5006 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5007 {   POPUPMENU *menu;
5008
5009     TRACE("(%p %p)\n", hMenu, lpmi);
5010
5011     if (lpmi && (menu = MENU_GetMenu(hMenu)))
5012     {
5013
5014         if (lpmi->fMask & MIM_BACKGROUND)
5015             lpmi->hbrBack = menu->hbrBack;
5016
5017         if (lpmi->fMask & MIM_HELPID)
5018             lpmi->dwContextHelpID = menu->dwContextHelpID;
5019
5020         if (lpmi->fMask & MIM_MAXHEIGHT)
5021             lpmi->cyMax = menu->cyMax;
5022
5023         if (lpmi->fMask & MIM_MENUDATA)
5024             lpmi->dwMenuData = menu->dwMenuData;
5025
5026         if (lpmi->fMask & MIM_STYLE)
5027             lpmi->dwStyle = menu->dwStyle;
5028
5029         return TRUE;
5030     }
5031     return FALSE;
5032 }
5033
5034
5035 /**********************************************************************
5036  *         SetMenuContextHelpId    (USER32.@)
5037  */
5038 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5039 {
5040     LPPOPUPMENU menu;
5041
5042     TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5043
5044     if ((menu = MENU_GetMenu(hMenu)))
5045     {
5046         menu->dwContextHelpID = dwContextHelpID;
5047         return TRUE;
5048     }
5049     return FALSE;
5050 }
5051
5052
5053 /**********************************************************************
5054  *         GetMenuContextHelpId    (USER32.@)
5055  */
5056 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5057 {
5058     LPPOPUPMENU menu;
5059
5060     TRACE("(%p)\n", hMenu);
5061
5062     if ((menu = MENU_GetMenu(hMenu)))
5063     {
5064         return menu->dwContextHelpID;
5065     }
5066     return 0;
5067 }
5068
5069 /**********************************************************************
5070  *         MenuItemFromPoint    (USER32.@)
5071  */
5072 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5073 {
5074     POPUPMENU *menu = MENU_GetMenu(hMenu);
5075     UINT pos;
5076
5077     /*FIXME: Do we have to handle hWnd here? */
5078     if (!menu) return -1;
5079     if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5080     return pos;
5081 }
5082
5083
5084 /**********************************************************************
5085  *           translate_accelerator
5086  */
5087 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5088                                    BYTE fVirt, WORD key, WORD cmd )
5089 {
5090     INT mask = 0;
5091     UINT mesg = 0;
5092
5093     if (wParam != key) return FALSE;
5094
5095     if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5096     if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5097     if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5098
5099     if (message == WM_CHAR || message == WM_SYSCHAR)
5100     {
5101         if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5102         {
5103             TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5104             goto found;
5105         }
5106     }
5107     else
5108     {
5109         if(fVirt & FVIRTKEY)
5110         {
5111             TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5112                           wParam, 0xff & HIWORD(lParam));
5113
5114             if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5115             TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5116         }
5117         else
5118         {
5119             if (!(lParam & 0x01000000))  /* no special_key */
5120             {
5121                 if ((fVirt & FALT) && (lParam & 0x20000000))
5122                 {                              /* ^^ ALT pressed */
5123                     TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5124                     goto found;
5125                 }
5126             }
5127         }
5128     }
5129     return FALSE;
5130
5131  found:
5132     if (message == WM_KEYUP || message == WM_SYSKEYUP)
5133         mesg = 1;
5134     else
5135     {
5136         HMENU hMenu, hSubMenu, hSysMenu;
5137         UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5138
5139         hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5140         hSysMenu = get_win_sys_menu( hWnd );
5141
5142         /* find menu item and ask application to initialize it */
5143         /* 1. in the system menu */
5144         hSubMenu = hSysMenu;
5145         nPos = cmd;
5146         if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5147         {
5148             if (GetCapture())
5149                 mesg = 2;
5150             if (!IsWindowEnabled(hWnd))
5151                 mesg = 3;
5152             else
5153             {
5154                 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5155                 if(hSubMenu != hSysMenu)
5156                 {
5157                     nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5158                     TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5159                     SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5160                 }
5161                 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5162             }
5163         }
5164         else /* 2. in the window's menu */
5165         {
5166             hSubMenu = hMenu;
5167             nPos = cmd;
5168             if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5169             {
5170                 if (GetCapture())
5171                     mesg = 2;
5172                 if (!IsWindowEnabled(hWnd))
5173                     mesg = 3;
5174                 else
5175                 {
5176                     SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5177                     if(hSubMenu != hMenu)
5178                     {
5179                         nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5180                         TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5181                         SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5182                     }
5183                     uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5184                 }
5185             }
5186         }
5187
5188         if (mesg == 0)
5189         {
5190             if (uSysStat != (UINT)-1)
5191             {
5192                 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5193                     mesg=4;
5194                 else
5195                     mesg=WM_SYSCOMMAND;
5196             }
5197             else
5198             {
5199                 if (uStat != (UINT)-1)
5200                 {
5201                     if (IsIconic(hWnd))
5202                         mesg=5;
5203                     else
5204                     {
5205                         if (uStat & (MF_DISABLED|MF_GRAYED))
5206                             mesg=6;
5207                         else
5208                             mesg=WM_COMMAND;
5209                     }
5210                 }
5211                 else
5212                     mesg=WM_COMMAND;
5213             }
5214         }
5215     }
5216
5217     if( mesg==WM_COMMAND )
5218     {
5219         TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5220         SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5221     }
5222     else if( mesg==WM_SYSCOMMAND )
5223     {
5224         TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5225         SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5226     }
5227     else
5228     {
5229         /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5230          *   #0: unknown (please report!)
5231          *   #1: for WM_KEYUP,WM_SYSKEYUP
5232          *   #2: mouse is captured
5233          *   #3: window is disabled
5234          *   #4: it's a disabled system menu option
5235          *   #5: it's a menu option, but window is iconic
5236          *   #6: it's a menu option, but disabled
5237          */
5238         TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5239         if(mesg==0)
5240             ERR_(accel)(" unknown reason - please report!\n");
5241     }
5242     return TRUE;
5243 }
5244
5245 /**********************************************************************
5246  *      TranslateAcceleratorA     (USER32.@)
5247  *      TranslateAccelerator      (USER32.@)
5248  */
5249 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5250 {
5251     /* YES, Accel16! */
5252     LPACCEL16 lpAccelTbl;
5253     int i;
5254     WPARAM wParam;
5255
5256     if (!hWnd || !msg) return 0;
5257
5258     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5259     {
5260         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5261         return 0;
5262     }
5263
5264     wParam = msg->wParam;
5265
5266     switch (msg->message)
5267     {
5268     case WM_KEYDOWN:
5269     case WM_SYSKEYDOWN:
5270         break;
5271
5272     case WM_CHAR:
5273     case WM_SYSCHAR:
5274         {
5275             char ch = LOWORD(wParam);
5276             WCHAR wch;
5277             MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5278             wParam = MAKEWPARAM(wch, HIWORD(wParam));
5279         }
5280         break;
5281
5282     default:
5283         return 0;
5284     }
5285
5286     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5287                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5288     i = 0;
5289     do
5290     {
5291         if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5292                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5293             return 1;
5294     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5295
5296     return 0;
5297 }
5298
5299 /**********************************************************************
5300  *      TranslateAcceleratorW     (USER32.@)
5301  */
5302 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5303 {
5304     /* YES, Accel16! */
5305     LPACCEL16 lpAccelTbl;
5306     int i;
5307
5308     if (!hWnd || !msg) return 0;
5309
5310     if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5311     {
5312         WARN_(accel)("invalid accel handle=%p\n", hAccel);
5313         return 0;
5314     }
5315
5316     switch (msg->message)
5317     {
5318     case WM_KEYDOWN:
5319     case WM_SYSKEYDOWN:
5320     case WM_CHAR:
5321     case WM_SYSCHAR:
5322         break;
5323
5324     default:
5325         return 0;
5326     }
5327
5328     TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5329                   hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5330     i = 0;
5331     do
5332     {
5333         if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5334                                    lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5335             return 1;
5336     } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5337
5338     return 0;
5339 }