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