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