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