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