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