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