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