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