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