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