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