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