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