Release 980628
[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 #include "windows.h"
20 #include "bitmap.h"
21 #include "gdi.h"
22 #include "sysmetrics.h"
23 #include "task.h"
24 #include "win.h"
25 #include "heap.h"
26 #include "menu.h"
27 #include "module.h"
28 #include "neexe.h"
29 #include "nonclient.h"
30 #include "user.h"
31 #include "message.h"
32 #include "graphics.h"
33 #include "resource.h"
34 #include "tweak.h"
35 #include "debug.h"
36
37
38 UINT32  MENU_BarItemTopNudge;
39 UINT32  MENU_BarItemLeftNudge;
40 UINT32  MENU_ItemTopNudge;
41 UINT32  MENU_ItemLeftNudge;
42 UINT32  MENU_HighlightTopNudge;
43 UINT32  MENU_HighlightLeftNudge;
44 UINT32  MENU_HighlightBottomNudge;
45 UINT32  MENU_HighlightRightNudge;
46
47 /* internal popup menu window messages */
48
49 #define MM_SETMENUHANDLE        (WM_USER + 0)
50 #define MM_GETMENUHANDLE        (WM_USER + 1)
51
52 /* Menu item structure */
53 typedef struct {
54     /* ----------- MENUITEMINFO Stuff ----------- */
55     UINT32 fType;               /* Item type. */
56     UINT32 fState;              /* Item state.  */
57     UINT32 wID;                 /* Item id.  */
58     HMENU32 hSubMenu;           /* Pop-up menu.  */
59     HBITMAP32 hCheckBit;        /* Bitmap when checked.  */
60     HBITMAP32 hUnCheckBit;      /* Bitmap when unchecked.  */
61     LPSTR text;                 /* Item text or bitmap handle.  */
62     DWORD dwItemData;           /* Application defined.  */
63     /* ----------- Wine stuff ----------- */
64     RECT32      rect;          /* Item area (relative to menu window) */
65     UINT32      xTab;          /* X position of text after Tab */
66 } MENUITEM;
67
68 /* Popup menu structure */
69 typedef struct {
70     WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
71     WORD        wMagic;       /* Magic number */
72     HQUEUE16    hTaskQ;       /* Task queue for this menu */
73     WORD        Width;        /* Width of the whole menu */
74     WORD        Height;       /* Height of the whole menu */
75     WORD        nItems;       /* Number of items in the menu */
76     HWND32      hWnd;         /* Window containing the menu */
77     MENUITEM   *items;        /* Array of menu items */
78     UINT32      FocusedItem;  /* Currently focused item */
79     WORD        defitem;      /* default item position. Unused (except for set/get)*/
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     UINT32      trackFlags;
91     HMENU32     hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
92     HMENU32     hTopMenu;     /* initial menu */
93     HWND32      hOwnerWnd;    /* where notifications are sent */
94     POINT32     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
108   /* popup menu shade thickness */
109 #define POPUP_XSHADE            4
110 #define POPUP_YSHADE            4
111
112   /* Space between 2 menu bar items */
113 #define MENU_BAR_ITEMS_SPACE 12
114
115   /* Minimum width of a tab character */
116 #define MENU_TAB_SPACE 8
117
118   /* Height of a separator item */
119 #define SEPARATOR_HEIGHT 5
120
121   /* (other menu->FocusedItem values give the position of the focused item) */
122 #define NO_SELECTED_ITEM  0xffff
123
124 #define MENU_ITEM_TYPE(flags) \
125   ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
126
127 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
128
129 #define IS_SYSTEM_MENU(menu)  \
130         (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
131 #define IS_SYSTEM_POPUP(menu) \
132         ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
133
134 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
135                    MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
136                    MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
137                    MF_POPUP | MF_SYSMENU | MF_HELP)
138 #define STATE_MASK (~TYPE_MASK)
139
140   /* Dimension of the menu bitmaps */
141 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
142 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
143
144 static HBITMAP32 hStdRadioCheck = 0;
145 static HBITMAP32 hStdCheck = 0;
146 static HBITMAP32 hStdMnArrow = 0;
147 static HBRUSH32 hShadeBrush = 0;
148 static HMENU32 MENU_DefSysPopup = 0;  /* Default system menu popup */
149
150 /* Use global popup window because there's no way 2 menus can
151  * be tracked at the same time.  */ 
152
153 static WND* pTopPopupWnd   = 0;
154 static UINT32 uSubPWndLevel = 0;
155
156   /* Flag set by EndMenu() to force an exit from menu tracking */
157 static BOOL32 fEndMenu = FALSE;
158
159
160 /***********************************************************************
161  *           debug_print_menuitem
162  *
163  * Print a menuitem in readable form.
164  */
165
166 #define debug_print_menuitem(pre, mp, post) \
167   if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
168
169 #define MENUOUT(text) \
170   dsprintf(menu, "%s%s", (count++ ? "," : ""), (text))
171
172 #define MENUFLAG(bit,text) \
173   do { \
174     if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
175   } while (0)
176
177 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp, 
178                                     const char *postfix)
179 {
180     dbg_decl_str(menu, 256);
181
182     if (mp) {
183         UINT32 flags = mp->fType;
184         int typ = MENU_ITEM_TYPE(flags);
185         dsprintf(menu, "{ ID=0x%x", mp->wID);
186         if (flags & MF_POPUP)
187             dsprintf(menu, ", Sub=0x%x", mp->hSubMenu);
188         if (flags) {
189             int count = 0;
190             dsprintf(menu, ", Typ=");
191             if (typ == MFT_STRING)
192                 /* Nothing */ ;
193             else if (typ == MFT_SEPARATOR)
194                 MENUOUT("sep");
195             else if (typ == MFT_OWNERDRAW)
196                 MENUOUT("own");
197             else if (typ == MFT_BITMAP)
198                 MENUOUT("bit");
199             else
200                 MENUOUT("???");
201             flags -= typ;
202
203             MENUFLAG(MF_POPUP, "pop");
204             MENUFLAG(MFT_MENUBARBREAK, "barbrk");
205             MENUFLAG(MFT_MENUBREAK, "brk");
206             MENUFLAG(MFT_RADIOCHECK, "radio");
207             MENUFLAG(MFT_RIGHTORDER, "rorder");
208             MENUFLAG(MF_SYSMENU, "sys");
209             MENUFLAG(MFT_RIGHTJUSTIFY, "right");
210
211             if (flags)
212                 dsprintf(menu, "+0x%x", flags);
213         }
214         flags = mp->fState;
215         if (flags) {
216             int count = 0;
217             dsprintf(menu, ", State=");
218             MENUFLAG(MFS_GRAYED, "grey");
219             MENUFLAG(MFS_DISABLED, "dis");
220             MENUFLAG(MFS_CHECKED, "check");
221             MENUFLAG(MFS_HILITE, "hi");
222             MENUFLAG(MF_USECHECKBITMAPS, "usebit");
223             MENUFLAG(MF_MOUSESELECT, "mouse");
224             if (flags)
225                 dsprintf(menu, "+0x%x", flags);
226         }
227         if (mp->hCheckBit)
228             dsprintf(menu, ", Chk=0x%x", mp->hCheckBit);
229         if (mp->hUnCheckBit)
230             dsprintf(menu, ", Unc=0x%x", mp->hUnCheckBit);
231
232         if (typ == MFT_STRING) {
233             if (mp->text)
234                 dsprintf(menu, ", Text=\"%s\"", mp->text);
235             else
236                 dsprintf(menu, ", Text=Null");
237         } else if (mp->text == NULL)
238             /* Nothing */ ;
239         else
240             dsprintf(menu, ", Text=%p", mp->text);
241         dsprintf(menu, " }");
242     } else {
243         dsprintf(menu, "NULL");
244     }
245
246     TRACE(menu, "%s %s %s\n", prefix, dbg_str(menu), postfix);
247 }
248
249 #undef MENUOUT
250 #undef MENUFLAG
251
252 /***********************************************************************
253  *           MENU_CopySysPopup
254  *
255  * Return the default system menu.
256  */
257 static HMENU32 MENU_CopySysPopup(void)
258 {
259     HMENU32 hMenu = LoadMenuIndirect32A(SYSRES_GetResPtr(SYSRES_MENU_SYSMENU));
260
261     if( hMenu ) {
262         POPUPMENU* menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
263         menu->wFlags |= MF_SYSMENU | MF_POPUP;
264     }
265     else {
266         hMenu = 0;
267         ERR(menu, "Unable to load default system menu\n" );
268     }
269
270     TRACE(menu, "returning %x.\n", hMenu );
271
272     return hMenu;
273 }
274
275
276 /**********************************************************************
277  *           MENU_GetSysMenu
278  *
279  * Create a copy of the system menu. System menu in Windows is
280  * a special menu-bar with the single entry - system menu popup.
281  * This popup is presented to the outside world as a "system menu". 
282  * However, the real system menu handle is sometimes seen in the 
283  * WM_MENUSELECT paramemters (and Word 6 likes it this way).
284  */
285 HMENU32 MENU_GetSysMenu( HWND32 hWnd, HMENU32 hPopupMenu )
286 {
287     HMENU32 hMenu;
288
289     if ((hMenu = CreateMenu32()))
290     {
291         POPUPMENU *menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
292         menu->wFlags = MF_SYSMENU;
293         menu->hWnd = hWnd;
294
295         if (hPopupMenu == (HMENU32)(-1))
296             hPopupMenu = MENU_CopySysPopup();
297         else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup; 
298
299         if (hPopupMenu)
300         {
301             InsertMenu32A( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
302
303             menu->items[0].fType = MF_SYSMENU | MF_POPUP;
304             menu->items[0].fState = 0;
305             menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hPopupMenu);
306             menu->wFlags |= MF_SYSMENU;
307
308             TRACE(menu,"GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
309             return hMenu;
310         }
311         DestroyMenu32( hMenu );
312     }
313     ERR(menu, "failed to load system menu!\n");
314     return 0;
315 }
316
317
318 /***********************************************************************
319  *           MENU_Init
320  *
321  * Menus initialisation.
322  */
323 BOOL32 MENU_Init()
324 {
325     HBITMAP32 hBitmap;
326     static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
327                                             0x55, 0, 0xAA, 0,
328                                             0x55, 0, 0xAA, 0,
329                                             0x55, 0, 0xAA, 0 };
330
331     /* Load menu bitmaps */
332     hStdCheck = LoadBitmap32A(0, MAKEINTRESOURCE32A(OBM_CHECK));
333     hStdRadioCheck = LoadBitmap32A(0, MAKEINTRESOURCE32A(OBM_RADIOCHECK));
334     hStdMnArrow = LoadBitmap32A(0, MAKEINTRESOURCE32A(OBM_MNARROW));
335
336     if (hStdCheck)
337     {
338         BITMAP32 bm;
339         GetObject32A( hStdCheck, sizeof(bm), &bm );
340         check_bitmap_width = bm.bmWidth;
341         check_bitmap_height = bm.bmHeight;
342     } else
343          return FALSE;
344
345     /* Assume that radio checks have the same size as regular check.  */
346     if (!hStdRadioCheck)
347          return FALSE;
348
349     if (hStdMnArrow)
350         {
351          BITMAP32 bm;
352             GetObject32A( hStdMnArrow, sizeof(bm), &bm );
353             arrow_bitmap_width = bm.bmWidth;
354             arrow_bitmap_height = bm.bmHeight;
355     } else
356          return FALSE;
357
358     if ((hBitmap = CreateBitmap32( 8, 8, 1, 1, shade_bits)))
359             {
360                 if((hShadeBrush = CreatePatternBrush32( hBitmap )))
361                 {
362                     DeleteObject32( hBitmap );
363               if ((MENU_DefSysPopup = MENU_CopySysPopup()))
364                    return TRUE;
365         }
366     }
367
368     return FALSE;
369 }
370
371 /***********************************************************************
372  *           MENU_InitSysMenuPopup
373  *
374  * Grey the appropriate items in System menu.
375  */
376 static void MENU_InitSysMenuPopup( HMENU32 hmenu, DWORD style, DWORD clsStyle )
377 {
378     BOOL32 gray;
379
380     gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
381     EnableMenuItem32( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
382     gray = ((style & WS_MAXIMIZE) != 0);
383     EnableMenuItem32( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
384     gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
385     EnableMenuItem32( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
386     gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
387     EnableMenuItem32( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
388     gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
389     EnableMenuItem32( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
390     gray = (clsStyle & CS_NOCLOSE) != 0;
391     EnableMenuItem32( hmenu, SC_CLOSE, (gray ? MF_GRAYED : MF_ENABLED) );
392 }
393
394
395 /******************************************************************************
396  *
397  *   UINT32  MENU_GetStartOfNextColumn(
398  *     HMENU32  hMenu )
399  *
400  *****************************************************************************/
401
402 static UINT32  MENU_GetStartOfNextColumn(
403     HMENU32  hMenu )
404 {
405     POPUPMENU  *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
406     UINT32  i = menu->FocusedItem + 1;
407
408     if(!menu)
409         return NO_SELECTED_ITEM;
410
411     if( i == NO_SELECTED_ITEM )
412         return i;
413
414     for( ; i < menu->nItems; ++i ) {
415         if (menu->items[i].fType & MF_MENUBARBREAK)
416             return i;
417     }
418
419     return NO_SELECTED_ITEM;
420 }
421
422
423 /******************************************************************************
424  *
425  *   UINT32  MENU_GetStartOfPrevColumn(
426  *     HMENU32  hMenu )
427  *
428  *****************************************************************************/
429
430 static UINT32  MENU_GetStartOfPrevColumn(
431     HMENU32  hMenu )
432 {
433     POPUPMENU const  *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
434     UINT32  i;
435
436     if( !menu )
437         return NO_SELECTED_ITEM;
438
439     if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
440         return NO_SELECTED_ITEM;
441
442     /* Find the start of the column */
443
444     for(i = menu->FocusedItem; i != 0 &&
445          !(menu->items[i].fType & MF_MENUBARBREAK);
446         --i); /* empty */
447
448     if(i == 0)
449         return NO_SELECTED_ITEM;
450
451     for(--i; i != 0; --i) {
452         if (menu->items[i].fType & MF_MENUBARBREAK)
453             break;
454     }
455
456     TRACE(menu, "ret %d.\n", i );
457
458     return i;
459 }
460
461
462
463 /***********************************************************************
464  *           MENU_FindItem
465  *
466  * Find a menu item. Return a pointer on the item, and modifies *hmenu
467  * in case the item was in a sub-menu.
468  */
469 static MENUITEM *MENU_FindItem( HMENU32 *hmenu, UINT32 *nPos, UINT32 wFlags )
470 {
471     POPUPMENU *menu;
472     UINT32 i;
473
474     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(*hmenu))) return NULL;
475     if (wFlags & MF_BYPOSITION)
476     {
477         if (*nPos >= menu->nItems) return NULL;
478         return &menu->items[*nPos];
479     }
480     else
481     {
482         MENUITEM *item = menu->items;
483         for (i = 0; i < menu->nItems; i++, item++)
484         {
485             if (item->wID == *nPos)
486             {
487                 *nPos = i;
488                 return item;
489             }
490             else if (item->fType & MF_POPUP)
491             {
492                 HMENU32 hsubmenu = item->hSubMenu;
493                 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
494                 if (subitem)
495                 {
496                     *hmenu = hsubmenu;
497                     return subitem;
498                 }
499             }
500         }
501     }
502     return NULL;
503 }
504
505 /***********************************************************************
506  *           MENU_FreeItemData
507  */
508 static void MENU_FreeItemData( MENUITEM* item )
509 {
510     /* delete text */
511     if (IS_STRING_ITEM(item->fType) && item->text)
512         HeapFree( SystemHeap, 0, item->text );
513 }
514
515 /***********************************************************************
516  *           MENU_FindItemByCoords
517  *
518  * Find the item at the specified coordinates (screen coords). Does 
519  * not work for child windows and therefore should not be called for 
520  * an arbitrary system menu.
521  */
522 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, 
523                                         POINT32 pt, UINT32 *pos )
524 {
525     MENUITEM *item;
526     WND *wndPtr;
527     UINT32 i;
528
529     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
530     pt.x -= wndPtr->rectWindow.left;
531     pt.y -= wndPtr->rectWindow.top;
532     item = menu->items;
533     for (i = 0; i < menu->nItems; i++, item++)
534     {
535         if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
536             (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
537         {
538             if (pos) *pos = i;
539             return item;
540         }
541     }
542     return NULL;
543 }
544
545
546 /***********************************************************************
547  *           MENU_FindItemByKey
548  *
549  * Find the menu item selected by a key press.
550  * Return item id, -1 if none, -2 if we should close the menu.
551  */
552 static UINT32 MENU_FindItemByKey( HWND32 hwndOwner, HMENU32 hmenu, 
553                                   UINT32 key, BOOL32 forceMenuChar )
554 {
555     TRACE(menu,"\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
556
557     if (!IsMenu32( hmenu )) 
558     {
559         WND* w = WIN_FindWndPtr(hwndOwner);
560         hmenu = GetSubMenu32(w->hSysMenu, 0);
561     }
562
563     if (hmenu)
564     {
565         POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
566         MENUITEM *item = menu->items;
567         LONG menuchar;
568
569         if( !forceMenuChar )
570         {
571              UINT32 i;
572
573              key = toupper(key);
574              for (i = 0; i < menu->nItems; i++, item++)
575              {
576                 if (item->text && (IS_STRING_ITEM(item->fType)))
577                 {
578                     char *p = strchr( item->text, '&' );
579                     if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
580                 }
581              }
582         }
583         menuchar = SendMessage32A( hwndOwner, WM_MENUCHAR, 
584                                    MAKEWPARAM( key, menu->wFlags ), hmenu );
585         if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
586         if (HIWORD(menuchar) == 1) return (UINT32)(-2);
587     }
588     return (UINT32)(-1);
589 }
590
591
592 /***********************************************************************
593  *           MENU_CalcItemSize
594  *
595  * Calculate the size of the menu item and store it in lpitem->rect.
596  */
597 static void MENU_CalcItemSize( HDC32 hdc, MENUITEM *lpitem, HWND32 hwndOwner,
598                                INT32 orgX, INT32 orgY, BOOL32 menuBar )
599 {
600     DWORD dwSize;
601     char *p;
602
603     TRACE(menu, "HDC 0x%x at (%d,%d)\n",
604                  hdc, orgX, orgY);
605     debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem, 
606                          (menuBar ? " (MenuBar)" : ""));
607
608     SetRect32( &lpitem->rect, orgX, orgY, orgX, orgY );
609
610     if (lpitem->fType & MF_OWNERDRAW)
611     {
612         MEASUREITEMSTRUCT32 mis;
613         mis.CtlType    = ODT_MENU;
614         mis.itemID     = lpitem->wID;
615         mis.itemData   = (DWORD)lpitem->text;
616         mis.itemHeight = 16;
617         mis.itemWidth  = 30;
618         SendMessage32A( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
619         lpitem->rect.bottom += mis.itemHeight;
620         lpitem->rect.right  += mis.itemWidth;
621         TRACE(menu, "%08x %dx%d\n",
622                      lpitem->wID, mis.itemWidth, mis.itemHeight);
623         return;
624     } 
625
626     if (lpitem->fType & MF_SEPARATOR)
627     {
628         lpitem->rect.bottom += SEPARATOR_HEIGHT;
629         return;
630     }
631
632     if (!menuBar)
633     {
634         lpitem->rect.right += 2 * check_bitmap_width;
635         if (lpitem->fType & MF_POPUP)
636             lpitem->rect.right += arrow_bitmap_width;
637     }
638
639     if (lpitem->fType & MF_BITMAP)
640     {
641         BITMAP32 bm;
642         if (GetObject32A( (HBITMAP32)lpitem->text, sizeof(bm), &bm ))
643         {
644             lpitem->rect.right  += bm.bmWidth;
645             lpitem->rect.bottom += bm.bmHeight;
646         }
647         return;
648     }
649     
650     /* If we get here, then it must be a text item */
651
652     if (IS_STRING_ITEM( lpitem->fType ))
653     {
654         dwSize = GetTextExtent( hdc, lpitem->text, strlen(lpitem->text) );
655         lpitem->rect.right  += LOWORD(dwSize);
656         if (TWEAK_Win95Look)
657             lpitem->rect.bottom += MAX (HIWORD(dwSize), sysMetrics[SM_CYMENU]- 1);
658         else
659             lpitem->rect.bottom += MAX( HIWORD(dwSize), SYSMETRICS_CYMENU );
660         lpitem->xTab = 0;
661
662         if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
663         else if ((p = strchr( lpitem->text, '\t' )) != NULL)
664         {
665             /* Item contains a tab (only meaningful in popup menus) */
666             lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + 
667                 LOWORD( GetTextExtent( hdc, lpitem->text,
668                                        (int)(p - lpitem->text) ));
669             lpitem->rect.right += MENU_TAB_SPACE;
670         }
671         else
672         {
673             if (strchr( lpitem->text, '\b' ))
674                 lpitem->rect.right += MENU_TAB_SPACE;
675             lpitem->xTab = lpitem->rect.right - check_bitmap_width 
676                            - arrow_bitmap_width;
677         }
678     }
679 }
680
681
682 /***********************************************************************
683  *           MENU_PopupMenuCalcSize
684  *
685  * Calculate the size of a popup menu.
686  */
687 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND32 hwndOwner )
688 {
689     MENUITEM *lpitem;
690     HDC32 hdc;
691     int start, i;
692     int orgX, orgY, maxX, maxTab, maxTabWidth;
693
694     lppop->Width = lppop->Height = 0;
695     if (lppop->nItems == 0) return;
696     hdc = GetDC32( 0 );
697     start = 0;
698     maxX = SYSMETRICS_CXBORDER;
699     while (start < lppop->nItems)
700     {
701         lpitem = &lppop->items[start];
702         orgX = maxX;
703         orgY = SYSMETRICS_CYBORDER;
704
705         maxTab = maxTabWidth = 0;
706
707           /* Parse items until column break or end of menu */
708         for (i = start; i < lppop->nItems; i++, lpitem++)
709         {
710             if ((i != start) &&
711                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
712
713             if(TWEAK_Win95Look)
714                 ++orgY;
715
716             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
717             if (lpitem->fType & MF_MENUBARBREAK) orgX++;
718             maxX = MAX( maxX, lpitem->rect.right );
719             orgY = lpitem->rect.bottom;
720             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
721             {
722                 maxTab = MAX( maxTab, lpitem->xTab );
723                 maxTabWidth = MAX(maxTabWidth,lpitem->rect.right-lpitem->xTab);
724             }
725         }
726
727           /* Finish the column (set all items to the largest width found) */
728         maxX = MAX( maxX, maxTab + maxTabWidth );
729         for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
730         {
731             lpitem->rect.right = maxX;
732             if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
733                 lpitem->xTab = maxTab;
734         }
735         lppop->Height = MAX( lppop->Height, orgY );
736     }
737
738     if(TWEAK_Win95Look)
739         lppop->Height++;
740
741     lppop->Width  = maxX;
742     ReleaseDC32( 0, hdc );
743 }
744
745
746 /***********************************************************************
747  *           MENU_MenuBarCalcSize
748  *
749  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
750  * height is off by 1 pixel which causes lengthy window relocations when
751  * active document window is maximized/restored.
752  *
753  * Calculate the size of the menu bar.
754  */
755 static void MENU_MenuBarCalcSize( HDC32 hdc, LPRECT32 lprect,
756                                   LPPOPUPMENU lppop, HWND32 hwndOwner )
757 {
758     MENUITEM *lpitem;
759     int start, i, orgX, orgY, maxY, helpPos;
760
761     if ((lprect == NULL) || (lppop == NULL)) return;
762     if (lppop->nItems == 0) return;
763     TRACE(menu,"MENU_MenuBarCalcSize left=%d top=%d right=%d bottom=%d\n", 
764                  lprect->left, lprect->top, lprect->right, lprect->bottom);
765     lppop->Width  = lprect->right - lprect->left;
766     lppop->Height = 0;
767     maxY = lprect->top;
768     start = 0;
769     helpPos = -1;
770     while (start < lppop->nItems)
771     {
772         lpitem = &lppop->items[start];
773         orgX = lprect->left;
774         orgY = maxY;
775
776           /* Parse items until line break or end of menu */
777         for (i = start; i < lppop->nItems; i++, lpitem++)
778         {
779             if ((helpPos == -1) && (lpitem->fType & MF_HELP)) helpPos = i;
780             if ((i != start) &&
781                 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
782
783             TRACE(menu, "calling MENU_CalcItemSize org=(%d, %d)\n", 
784                          orgX, orgY );
785             debug_print_menuitem ("  item: ", lpitem, "");
786             MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
787             if (lpitem->rect.right > lprect->right)
788             {
789                 if (i != start) break;
790                 else lpitem->rect.right = lprect->right;
791             }
792             maxY = MAX( maxY, lpitem->rect.bottom );
793             orgX = lpitem->rect.right;
794         }
795
796           /* Finish the line (set all items to the largest height found) */
797         while (start < i) lppop->items[start++].rect.bottom = maxY;
798     }
799
800     lprect->bottom = maxY;
801     lppop->Height = lprect->bottom - lprect->top;
802
803       /* Flush right all items between the MF_HELP and the last item */
804       /* (if several lines, only move the last line) */
805     if (helpPos != -1)
806     {
807         lpitem = &lppop->items[lppop->nItems-1];
808         orgY = lpitem->rect.top;
809         orgX = lprect->right;
810         for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
811         {
812             if (lpitem->rect.top != orgY) break;    /* Other line */
813             if (lpitem->rect.right >= orgX) break;  /* Too far right already */
814             lpitem->rect.left += orgX - lpitem->rect.right;
815             lpitem->rect.right = orgX;
816             orgX = lpitem->rect.left;
817         }
818     }
819 }
820
821 /***********************************************************************
822  *           MENU_DrawMenuItem
823  *
824  * Draw a single menu item.
825  */
826 static void MENU_DrawMenuItem( HWND32 hwnd, HDC32 hdc, MENUITEM *lpitem,
827                                UINT32 height, BOOL32 menuBar, UINT32 odaction )
828 {
829     RECT32 rect;
830
831     debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
832
833     if (lpitem->fType & MF_SYSMENU)
834     {
835         if( !IsIconic32(hwnd) ) {
836             if(TWEAK_Win95Look)
837                 NC_DrawSysButton95( hwnd, hdc,
838                                     lpitem->fState &
839                                     (MF_HILITE | MF_MOUSESELECT) );
840             else
841                 NC_DrawSysButton( hwnd, hdc, 
842                                   lpitem->fState &
843                                   (MF_HILITE | MF_MOUSESELECT) );
844         }
845
846         return;
847     }
848
849     if (lpitem->fType & MF_OWNERDRAW)
850     {
851         DRAWITEMSTRUCT32 dis;
852
853         dis.CtlType   = ODT_MENU;
854         dis.itemID    = lpitem->wID;
855         dis.itemData  = (DWORD)lpitem->text;
856         dis.itemState = 0;
857         if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
858         if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED;
859         if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
860         dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
861         dis.hwndItem   = hwnd;
862         dis.hDC        = hdc;
863         dis.rcItem     = lpitem->rect;
864         TRACE(menu, "Ownerdraw: itemID=%d, itemState=%d, itemAction=%d, "
865               "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n",dis.itemID,
866               dis.itemState, dis.itemAction, dis.hwndItem, dis.hDC,
867               dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
868               dis.rcItem.bottom );
869         SendMessage32A( GetWindow32(hwnd,GW_OWNER), WM_DRAWITEM, 0, (LPARAM)&dis );
870         return;
871     }
872
873     if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
874     rect = lpitem->rect;
875
876     /* Draw the background */
877     if(TWEAK_Win95Look) {
878         rect.left += 2;
879         rect.right -= 2;
880
881         /*
882         if(menuBar) {
883             --rect.left;
884             ++rect.bottom;
885             --rect.top;
886         }
887         InflateRect32( &rect, -1, -1 );
888         */
889     }
890
891     if (lpitem->fState & MF_HILITE)
892         FillRect32( hdc, &rect, GetSysColorBrush32(COLOR_HIGHLIGHT) );
893     else
894         FillRect32( hdc, &rect, GetSysColorBrush32(COLOR_MENU) );
895
896     SetBkMode32( hdc, TRANSPARENT );
897
898       /* Draw the separator bar (if any) */
899
900     if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
901     {
902         if(TWEAK_Win95Look)
903             TWEAK_DrawMenuSeparatorVert95(hdc, rect.left - 1, 3, height - 3);
904         else {
905             SelectObject32( hdc, GetSysColorPen32(COLOR_WINDOWFRAME) );
906             MoveTo( hdc, rect.left, 0 );
907             LineTo32( hdc, rect.left, height );
908         }
909     }
910     if (lpitem->fType & MF_SEPARATOR)
911     {
912         if(TWEAK_Win95Look)
913             TWEAK_DrawMenuSeparatorHoriz95(hdc, rect.left + 1,
914                                            rect.top + SEPARATOR_HEIGHT / 2 + 1,
915                                            rect.right - 1);
916         else {
917             SelectObject32( hdc, GetSysColorPen32(COLOR_WINDOWFRAME) );
918             MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
919             LineTo32( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
920         }
921
922         return;
923     }
924
925       /* Setup colors */
926
927     if (lpitem->fState & MF_HILITE)
928     {
929         if (lpitem->fState & MF_GRAYED)
930             SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
931         else
932             SetTextColor32( hdc, GetSysColor32( COLOR_HIGHLIGHTTEXT ) );
933         SetBkColor32( hdc, GetSysColor32( COLOR_HIGHLIGHT ) );
934     }
935     else
936     {
937         if (lpitem->fState & MF_GRAYED)
938             SetTextColor32( hdc, GetSysColor32( COLOR_GRAYTEXT ) );
939         else
940             SetTextColor32( hdc, GetSysColor32( COLOR_MENUTEXT ) );
941         SetBkColor32( hdc, GetSysColor32( COLOR_MENU ) );
942     }
943
944     if (!menuBar)
945     {
946         INT32   y = rect.top + rect.bottom;
947
948           /* Draw the check mark
949            *
950            * Custom checkmark bitmaps are monochrome but not always 1bpp. 
951            * In this case we want GRAPH_DrawBitmap() to copy a plane which 
952            * is 1 for a white pixel and 0 for a black one. 
953            */
954
955         if (lpitem->fState & MF_CHECKED)
956         {
957             HBITMAP32 bm =
958                  lpitem->hCheckBit ? lpitem->hCheckBit :
959                  ((lpitem->fType & MFT_RADIOCHECK)
960                   ? hStdRadioCheck : hStdCheck);
961             GRAPH_DrawBitmap( hdc, bm, rect.left,
962                               (y - check_bitmap_height) / 2,
963                               0, 0, check_bitmap_width,
964                               check_bitmap_height, TRUE );
965         } else if (lpitem->hUnCheckBit)
966             GRAPH_DrawBitmap( hdc, lpitem->hUnCheckBit, rect.left,
967                               (y - check_bitmap_height) / 2, 0, 0,
968                               check_bitmap_width, check_bitmap_height, TRUE );
969
970           /* Draw the popup-menu arrow */
971
972         if (lpitem->fType & MF_POPUP)
973         {
974             GRAPH_DrawBitmap( hdc, hStdMnArrow,
975                               rect.right-arrow_bitmap_width-1,
976                               (y - arrow_bitmap_height) / 2, 0, 0, 
977                               arrow_bitmap_width, arrow_bitmap_height, FALSE );
978         }
979
980         rect.left += check_bitmap_width;
981         rect.right -= arrow_bitmap_width;
982     }
983
984       /* Draw the item text or bitmap */
985
986     if (lpitem->fType & MF_BITMAP)
987     {
988         GRAPH_DrawBitmap( hdc, (HBITMAP32)lpitem->text,
989                           rect.left, rect.top, 0, 0,
990                           rect.right-rect.left, rect.bottom-rect.top, FALSE );
991         return;
992     }
993     /* No bitmap - process text if present */
994     else if (IS_STRING_ITEM(lpitem->fType))
995     {
996         register int i;
997
998         if (menuBar)
999         {
1000             rect.left += MENU_BAR_ITEMS_SPACE / 2;
1001             rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1002             i = strlen( lpitem->text );
1003
1004             rect.top += MENU_BarItemTopNudge;
1005             rect.left += MENU_BarItemLeftNudge;
1006         }
1007         else
1008         {
1009             for (i = 0; lpitem->text[i]; i++)
1010                 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1011                     break;
1012
1013             rect.top += MENU_ItemTopNudge;
1014             rect.left += MENU_ItemLeftNudge;
1015         }
1016
1017         if(!TWEAK_Win95Look || !(lpitem->fState & MF_GRAYED)) {
1018             DrawText32A( hdc, lpitem->text, i, &rect,
1019                          DT_LEFT | DT_VCENTER | DT_SINGLELINE );
1020         }
1021         else {
1022             if (!(lpitem->fState & MF_HILITE))
1023             {
1024                 ++rect.left;
1025                 ++rect.top;
1026                 ++rect.right;
1027                 ++rect.bottom;
1028                 SetTextColor32(hdc, RGB(0xff, 0xff, 0xff));
1029                 DrawText32A( hdc, lpitem->text, i, &rect,
1030                              DT_LEFT | DT_VCENTER | DT_SINGLELINE );
1031                 --rect.left;
1032                 --rect.top;
1033                 --rect.right;
1034                 --rect.bottom;
1035             }
1036             SetTextColor32(hdc, RGB(0x80, 0x80, 0x80));
1037             DrawText32A( hdc, lpitem->text, i, &rect,
1038                          DT_LEFT | DT_VCENTER | DT_SINGLELINE );
1039         }
1040
1041         if (lpitem->text[i])  /* There's a tab or flush-right char */
1042         {
1043             if (lpitem->text[i] == '\t')
1044             {
1045                 rect.left = lpitem->xTab;
1046                 DrawText32A( hdc, lpitem->text + i + 1, -1, &rect,
1047                              DT_LEFT | DT_VCENTER | DT_SINGLELINE );
1048             }
1049             else DrawText32A( hdc, lpitem->text + i + 1, -1, &rect,
1050                               DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
1051         }
1052     }
1053 }
1054
1055
1056 /***********************************************************************
1057  *           MENU_DrawPopupMenu
1058  *
1059  * Paint a popup menu.
1060  */
1061 static void MENU_DrawPopupMenu( HWND32 hwnd, HDC32 hdc, HMENU32 hmenu )
1062 {
1063     HBRUSH32 hPrevBrush = 0;
1064     RECT32 rect;
1065
1066     GetClientRect32( hwnd, &rect );
1067
1068 /*    if(!TWEAK_Win95Look) { */
1069         rect.bottom -= POPUP_YSHADE * SYSMETRICS_CYBORDER;
1070         rect.right -= POPUP_XSHADE * SYSMETRICS_CXBORDER;
1071 /*    } */
1072
1073     if((hPrevBrush = SelectObject32( hdc, GetSysColorBrush32(COLOR_MENU) )))
1074     {
1075         HPEN32 hPrevPen;
1076         
1077         Rectangle32( hdc, rect.left, rect.top, rect.right, rect.bottom );
1078
1079         hPrevPen = SelectObject32( hdc, GetStockObject32( NULL_PEN ) );
1080         if( hPrevPen )
1081         {
1082             INT32 ropPrev, i;
1083             POPUPMENU *menu;
1084
1085             /* draw 3-d shade */
1086             if(!TWEAK_Win95Look) {
1087                 SelectObject32( hdc, hShadeBrush );
1088                 SetBkMode32( hdc, TRANSPARENT );
1089                 ropPrev = SetROP232( hdc, R2_MASKPEN );
1090
1091                 i = rect.right;         /* why SetBrushOrg() doesn't? */
1092                 PatBlt32( hdc, i & 0xfffffffe,
1093                           rect.top + POPUP_YSHADE*SYSMETRICS_CYBORDER, 
1094                           i%2 + POPUP_XSHADE*SYSMETRICS_CXBORDER,
1095                           rect.bottom - rect.top, 0x00a000c9 );
1096                 i = rect.bottom;
1097                 PatBlt32( hdc, rect.left + POPUP_XSHADE*SYSMETRICS_CXBORDER,
1098                           i & 0xfffffffe,rect.right - rect.left,
1099                           i%2 + POPUP_YSHADE*SYSMETRICS_CYBORDER, 0x00a000c9 );
1100                 SelectObject32( hdc, hPrevPen );
1101                 SelectObject32( hdc, hPrevBrush );
1102                 SetROP232( hdc, ropPrev );
1103             }
1104             else
1105                 TWEAK_DrawReliefRect95(hdc, &rect);
1106
1107             /* draw menu items */
1108
1109             menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1110             if (menu && menu->nItems)
1111             {
1112                 MENUITEM *item;
1113                 UINT32 u;
1114
1115                 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1116                     MENU_DrawMenuItem( hwnd, hdc, item, menu->Height, FALSE,
1117                                        ODA_DRAWENTIRE );
1118
1119             }
1120         } else SelectObject32( hdc, hPrevBrush );
1121     }
1122 }
1123
1124
1125 /***********************************************************************
1126  *           MENU_DrawMenuBar
1127  *
1128  * Paint a menu bar. Returns the height of the menu bar.
1129  */
1130 UINT32 MENU_DrawMenuBar( HDC32 hDC, LPRECT32 lprect, HWND32 hwnd,
1131                          BOOL32 suppress_draw)
1132 {
1133     LPPOPUPMENU lppop;
1134     UINT32 i;
1135     WND *wndPtr = WIN_FindWndPtr( hwnd );
1136     
1137     lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR( (HMENU16)wndPtr->wIDmenu );
1138     if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
1139     TRACE(menu,"(%04x, %p, %p); !\n", 
1140                  hDC, lprect, lppop);
1141     if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1142     lprect->bottom = lprect->top + lppop->Height;
1143     if (suppress_draw) return lppop->Height;
1144     
1145     FillRect32(hDC, lprect, GetSysColorBrush32(COLOR_MENU) );
1146
1147     if(!TWEAK_Win95Look) {
1148         SelectObject32( hDC, GetSysColorPen32(COLOR_WINDOWFRAME) );
1149         MoveTo( hDC, lprect->left, lprect->bottom );
1150         LineTo32( hDC, lprect->right, lprect->bottom );
1151     }
1152     else {
1153         SelectObject32( hDC, GetSysColorPen32(COLOR_3DFACE));
1154         MoveTo( hDC, lprect->left, lprect->bottom );
1155         LineTo32( hDC, lprect->right, lprect->bottom );
1156     }
1157
1158     if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
1159     for (i = 0; i < lppop->nItems; i++)
1160     {
1161         MENU_DrawMenuItem( hwnd, hDC, &lppop->items[i], lppop->Height, TRUE,
1162                            ODA_DRAWENTIRE );
1163     }
1164     return lppop->Height;
1165
1166
1167
1168 /***********************************************************************
1169  *           MENU_PatchResidentPopup
1170  */
1171 BOOL32 MENU_PatchResidentPopup( HQUEUE16 checkQueue, WND* checkWnd )
1172 {
1173     if( pTopPopupWnd )
1174     {
1175         HTASK16 hTask = 0;
1176
1177         TRACE(menu,"patching resident popup: %04x %04x [%04x %04x]\n", 
1178                 checkQueue, checkWnd ? checkWnd->hwndSelf : 0, pTopPopupWnd->hmemTaskQ, 
1179                 pTopPopupWnd->owner ? pTopPopupWnd->owner->hwndSelf : 0);
1180
1181         switch( checkQueue )
1182         {
1183             case 0: /* checkWnd is the new popup owner */
1184                  if( checkWnd )
1185                  {
1186                      pTopPopupWnd->owner = checkWnd;
1187                      if( pTopPopupWnd->hmemTaskQ != checkWnd->hmemTaskQ )
1188                          hTask = QUEUE_GetQueueTask( checkWnd->hmemTaskQ );
1189                  }
1190                  break;
1191
1192             case 0xFFFF: /* checkWnd is destroyed */
1193                  if( pTopPopupWnd->owner == checkWnd )
1194                      pTopPopupWnd->owner = NULL;
1195                  return TRUE; 
1196
1197             default: /* checkQueue is exiting */
1198                  if( pTopPopupWnd->hmemTaskQ == checkQueue )
1199                  {
1200                      hTask = QUEUE_GetQueueTask( pTopPopupWnd->hmemTaskQ );
1201                      hTask = TASK_GetNextTask( hTask );
1202                  }
1203                  break;
1204         }
1205
1206         if( hTask )
1207         {
1208             TDB* task = (TDB*)GlobalLock16( hTask );
1209             if( task )
1210             {
1211                 pTopPopupWnd->hInstance = task->hInstance;
1212                 pTopPopupWnd->hmemTaskQ = task->hQueue;
1213                 return TRUE;
1214             } 
1215             else WARN(menu,"failed to patch resident popup.\n");
1216         } 
1217     }
1218     return FALSE;
1219 }
1220
1221 /***********************************************************************
1222  *           MENU_ShowPopup
1223  *
1224  * Display a popup menu.
1225  */
1226 static BOOL32 MENU_ShowPopup( HWND32 hwndOwner, HMENU32 hmenu, UINT32 id,
1227                               INT32 x, INT32 y, INT32 xanchor, INT32 yanchor )
1228 {
1229     POPUPMENU   *menu;
1230     WND         *wndOwner = NULL;
1231
1232     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
1233     if (menu->FocusedItem != NO_SELECTED_ITEM)
1234     {
1235         menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1236         menu->FocusedItem = NO_SELECTED_ITEM;
1237     }
1238
1239     if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1240     {
1241         UINT32  width, height;
1242
1243         MENU_PopupMenuCalcSize( menu, hwndOwner );
1244
1245         /* adjust popup menu pos so that it fits within the desktop */
1246
1247         width = menu->Width + SYSMETRICS_CXBORDER;
1248         height = menu->Height + SYSMETRICS_CYBORDER; 
1249
1250         if( x + width > SYSMETRICS_CXSCREEN )
1251         {
1252             if( xanchor )
1253                 x -= width - xanchor;
1254             if( x + width > SYSMETRICS_CXSCREEN)
1255                 x = SYSMETRICS_CXSCREEN - width;
1256         }
1257         if( x < 0 ) x = 0;
1258
1259         if( y + height > SYSMETRICS_CYSCREEN )
1260         { 
1261             if( yanchor )
1262                 y -= height + yanchor;
1263             if( y + height > SYSMETRICS_CYSCREEN )
1264                 y = SYSMETRICS_CYSCREEN - height;
1265         }
1266         if( y < 0 ) y = 0;
1267
1268         width += POPUP_XSHADE * SYSMETRICS_CXBORDER;    /* add space for shading */
1269         height += POPUP_YSHADE * SYSMETRICS_CYBORDER;
1270
1271         /* NOTE: In Windows, top menu popup is not owned. */
1272         if (!pTopPopupWnd)      /* create top level popup menu window */
1273         {
1274             assert( uSubPWndLevel == 0 );
1275
1276             pTopPopupWnd = WIN_FindWndPtr(CreateWindow32A( POPUPMENU_CLASS_ATOM, NULL,
1277                                           WS_POPUP, x, y, width, height,
1278                                           hwndOwner, 0, wndOwner->hInstance,
1279                                           (LPVOID)hmenu ));
1280             if (!pTopPopupWnd) return FALSE;
1281             menu->hWnd = pTopPopupWnd->hwndSelf;
1282         } 
1283         else
1284             if( uSubPWndLevel )
1285             {
1286                 /* create a new window for the submenu */
1287
1288                 menu->hWnd = CreateWindow32A( POPUPMENU_CLASS_ATOM, NULL,
1289                                           WS_POPUP, x, y, width, height,
1290                                           menu->hWnd, 0, wndOwner->hInstance,
1291                                           (LPVOID)hmenu );
1292                 if( !menu->hWnd ) return FALSE;
1293             }
1294             else /* top level popup menu window already exists */
1295             {
1296                 menu->hWnd = pTopPopupWnd->hwndSelf;
1297
1298                 MENU_PatchResidentPopup( 0, wndOwner );
1299                 SendMessage16( pTopPopupWnd->hwndSelf, MM_SETMENUHANDLE, (WPARAM16)hmenu, 0L);  
1300
1301                 /* adjust its size */
1302
1303                 SetWindowPos32( menu->hWnd, 0, x, y, width, height,
1304                                 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
1305             }
1306
1307         uSubPWndLevel++;        /* menu level counter */
1308
1309       /* Display the window */
1310
1311         SetWindowPos32( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1312                         SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1313         UpdateWindow32( menu->hWnd );
1314         return TRUE;
1315     }
1316     return FALSE;
1317 }
1318
1319
1320 /***********************************************************************
1321  *           MENU_SelectItem
1322  */
1323 static void MENU_SelectItem( HWND32 hwndOwner, HMENU32 hmenu, UINT32 wIndex,
1324                              BOOL32 sendMenuSelect )
1325 {
1326     LPPOPUPMENU lppop;
1327     HDC32 hdc;
1328
1329     lppop = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1330     if (!lppop->nItems) return;
1331
1332     if ((wIndex != NO_SELECTED_ITEM) && 
1333         (lppop->items[wIndex].fType & MF_SEPARATOR))
1334         wIndex = NO_SELECTED_ITEM;
1335
1336     if (lppop->FocusedItem == wIndex) return;
1337     if (lppop->wFlags & MF_POPUP) hdc = GetDC32( lppop->hWnd );
1338     else hdc = GetDCEx32( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1339
1340       /* Clear previous highlighted item */
1341     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
1342     {
1343         lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1344         MENU_DrawMenuItem(lppop->hWnd,hdc,&lppop->items[lppop->FocusedItem],
1345                           lppop->Height, !(lppop->wFlags & MF_POPUP),
1346                           ODA_SELECT );
1347     }
1348
1349       /* Highlight new item (if any) */
1350     lppop->FocusedItem = wIndex;
1351     if (lppop->FocusedItem != NO_SELECTED_ITEM) 
1352     {
1353         lppop->items[lppop->FocusedItem].fState |= MF_HILITE;
1354         MENU_DrawMenuItem( lppop->hWnd, hdc, &lppop->items[lppop->FocusedItem],
1355                            lppop->Height, !(lppop->wFlags & MF_POPUP),
1356                            ODA_SELECT );
1357         if (sendMenuSelect)
1358         {
1359             MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1360             SendMessage16( hwndOwner, WM_MENUSELECT, ip->wID,
1361                            MAKELONG(ip->fType | (ip->fState | MF_MOUSESELECT),
1362                                     hmenu) );
1363         }
1364     }
1365     else if (sendMenuSelect)
1366         SendMessage16( hwndOwner, WM_MENUSELECT, hmenu,
1367                        MAKELONG( lppop->wFlags | MF_MOUSESELECT, hmenu ) ); 
1368
1369     ReleaseDC32( lppop->hWnd, hdc );
1370 }
1371
1372
1373 /***********************************************************************
1374  *           MENU_MoveSelection
1375  *
1376  * Moves currently selected item according to the offset parameter.
1377  * If there is no selection then it should select the last item if
1378  * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1379  */
1380 static void MENU_MoveSelection( HWND32 hwndOwner, HMENU32 hmenu, INT32 offset )
1381 {
1382     INT32 i;
1383     POPUPMENU *menu;
1384
1385     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1386     if (!menu->items) return;
1387
1388     if ( menu->FocusedItem != NO_SELECTED_ITEM )
1389     {
1390         if( menu->nItems == 1 ) return; else
1391         for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems 
1392                                             ; i += offset)
1393             if (!(menu->items[i].fType & MF_SEPARATOR))
1394             {
1395                 MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
1396                 return;
1397             }
1398     }
1399
1400     for ( i = (offset > 0) ? 0 : menu->nItems - 1; 
1401                   i >= 0 && i < menu->nItems ; i += offset)
1402         if (!(menu->items[i].fType & MF_SEPARATOR))
1403         {
1404             MENU_SelectItem( hwndOwner, hmenu, i, TRUE );
1405             return;
1406         }
1407 }
1408
1409
1410 /**********************************************************************
1411  *         MENU_SetItemData
1412  *
1413  * Set an item flags, id and text ptr. Called by InsertMenu() and
1414  * ModifyMenu().
1415  */
1416 static BOOL32 MENU_SetItemData( MENUITEM *item, UINT32 flags, UINT32 id,
1417                                 LPCSTR str )
1418 {
1419     LPSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1420
1421     debug_print_menuitem("MENU_SetItemData from: ", item, "");
1422
1423     if (IS_STRING_ITEM(flags))
1424     {
1425         if (!str || !*str)
1426         {
1427             flags |= MF_SEPARATOR;
1428             item->text = NULL;
1429         }
1430         else
1431         {
1432             LPSTR text;
1433             /* Item beginning with a backspace is a help item */
1434             if (*str == '\b')
1435             {
1436                 flags |= MF_HELP;
1437                 str++;
1438             }
1439             if (!(text = HEAP_strdupA( SystemHeap, 0, str ))) return FALSE;
1440             item->text = text;
1441         }
1442     }
1443     else if (flags & MF_BITMAP) item->text = (LPSTR)(HBITMAP32)LOWORD(str);
1444     else item->text = NULL;
1445
1446     if (flags & MF_OWNERDRAW) 
1447         item->dwItemData = (DWORD)str;
1448     else
1449         item->dwItemData = 0;
1450
1451     if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1452         DestroyMenu32( item->hSubMenu );   /* ModifyMenu() spec */
1453
1454     if (flags & MF_POPUP)
1455     {
1456         POPUPMENU *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR((UINT16)id);
1457         if (IS_A_MENU(menu)) menu->wFlags |= MF_POPUP;
1458         else
1459         {
1460             item->wID = 0;
1461             item->hSubMenu = 0;
1462             item->fType = 0;
1463             item->fState = 0;
1464             return FALSE;
1465         }
1466     } 
1467
1468     item->wID = id;
1469     if (flags & MF_POPUP)
1470       item->hSubMenu = id;
1471
1472     if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1473       flags |= MF_POPUP; /* keep popup */
1474
1475     item->fType = flags & TYPE_MASK;
1476     item->fState = (flags & STATE_MASK) &
1477         ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1478
1479     SetRectEmpty32( &item->rect );
1480     if (prevText) HeapFree( SystemHeap, 0, prevText );
1481
1482     debug_print_menuitem("MENU_SetItemData to  : ", item, "");
1483     return TRUE;
1484 }
1485
1486
1487 /**********************************************************************
1488  *         MENU_InsertItem
1489  *
1490  * Insert a new item into a menu.
1491  */
1492 static MENUITEM *MENU_InsertItem( HMENU32 hMenu, UINT32 pos, UINT32 flags )
1493 {
1494     MENUITEM *newItems;
1495     POPUPMENU *menu;
1496
1497     if (!(menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu))) 
1498     {
1499         WARN(menu, "%04x not a menu handle\n",
1500                       hMenu );
1501         return NULL;
1502     }
1503
1504     /* Find where to insert new item */
1505
1506     if ((flags & MF_BYPOSITION) &&
1507         ((pos == (UINT32)-1) || (pos == menu->nItems)))
1508     {
1509         /* Special case: append to menu */
1510         /* Some programs specify the menu length to do that */
1511         pos = menu->nItems;
1512     }
1513     else
1514     {
1515         if (!MENU_FindItem( &hMenu, &pos, flags )) 
1516         {
1517             WARN(menu, "item %x not found\n",
1518                           pos );
1519             return NULL;
1520         }
1521         if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu)))
1522         {
1523             WARN(menu,"%04x not a menu handle\n",
1524                          hMenu);
1525             return NULL;
1526         }
1527     }
1528
1529     /* Create new items array */
1530
1531     newItems = HeapAlloc( SystemHeap, 0, sizeof(MENUITEM) * (menu->nItems+1) );
1532     if (!newItems)
1533     {
1534         WARN(menu, "allocation failed\n" );
1535         return NULL;
1536     }
1537     if (menu->nItems > 0)
1538     {
1539           /* Copy the old array into the new */
1540         if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1541         if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1542                                         (menu->nItems-pos)*sizeof(MENUITEM) );
1543         HeapFree( SystemHeap, 0, menu->items );
1544     }
1545     menu->items = newItems;
1546     menu->nItems++;
1547     memset( &newItems[pos], 0, sizeof(*newItems) );
1548     return &newItems[pos];
1549 }
1550
1551
1552 /**********************************************************************
1553  *         MENU_ParseResource
1554  *
1555  * Parse a standard menu resource and add items to the menu.
1556  * Return a pointer to the end of the resource.
1557  */
1558 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU32 hMenu, BOOL32 unicode )
1559 {
1560     WORD flags, id = 0;
1561     LPCSTR str;
1562
1563     do
1564     {
1565         flags = GET_WORD(res);
1566         res += sizeof(WORD);
1567         if (!(flags & MF_POPUP))
1568         {
1569             id = GET_WORD(res);
1570             res += sizeof(WORD);
1571         }
1572         if (!IS_STRING_ITEM(flags))
1573             ERR(menu, "not a string item %04x\n", flags );
1574         str = res;
1575         if (!unicode) res += strlen(str) + 1;
1576         else res += (lstrlen32W((LPCWSTR)str) + 1) * sizeof(WCHAR);
1577         if (flags & MF_POPUP)
1578         {
1579             HMENU32 hSubMenu = CreatePopupMenu32();
1580             if (!hSubMenu) return NULL;
1581             if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1582                 return NULL;
1583             if (!unicode) AppendMenu32A( hMenu, flags, (UINT32)hSubMenu, str );
1584             else AppendMenu32W( hMenu, flags, (UINT32)hSubMenu, (LPCWSTR)str );
1585         }
1586         else  /* Not a popup */
1587         {
1588             if (!unicode) AppendMenu32A( hMenu, flags, id, *str ? str : NULL );
1589             else AppendMenu32W( hMenu, flags, id,
1590                                 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1591         }
1592     } while (!(flags & MF_END));
1593     return res;
1594 }
1595
1596
1597 /**********************************************************************
1598  *         MENUEX_ParseResource
1599  *
1600  * Parse an extended menu resource and add items to the menu.
1601  * Return a pointer to the end of the resource.
1602  */
1603 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU32 hMenu)
1604 {
1605     WORD resinfo;
1606     do {
1607         MENUITEMINFO32W mii;
1608
1609         mii.cbSize = sizeof(mii);
1610         mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1611         mii.fType = GET_DWORD(res);
1612         res += sizeof(DWORD);
1613         mii.fState = GET_DWORD(res);
1614         res += sizeof(DWORD);
1615         mii.wID = GET_DWORD(res);
1616         res += sizeof(DWORD);
1617         resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
1618         res += sizeof(WORD);
1619         /* Align the text on a word boundary.  */
1620         res += (~((int)res - 1)) & 1;
1621         mii.dwTypeData = (LPWSTR) res;
1622         res += (1 + lstrlen32W(mii.dwTypeData)) * sizeof(WCHAR);
1623         /* Align the following fields on a dword boundary.  */
1624         res += (~((int)res - 1)) & 3;
1625
1626         /* FIXME: This is inefficient and cannot be optimised away by gcc.  */
1627         {
1628             LPSTR newstr = HEAP_strdupWtoA(GetProcessHeap(),
1629                                            0, mii.dwTypeData);
1630             TRACE(menu, "Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1631                          mii.fType, mii.fState, mii.wID, resinfo, newstr);
1632             HeapFree( GetProcessHeap(), 0, newstr );
1633         }
1634
1635         if (resinfo & 1) {      /* Pop-up? */
1636             DWORD helpid = GET_DWORD(res); /* FIXME: use this.  */
1637             res += sizeof(DWORD);
1638             mii.hSubMenu = CreatePopupMenu32();
1639             if (!mii.hSubMenu)
1640                 return NULL;
1641             if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1642                 DestroyMenu32(mii.hSubMenu);
1643                 return NULL;
1644         }
1645             mii.fMask |= MIIM_SUBMENU;
1646             mii.fType |= MF_POPUP;
1647         }
1648         InsertMenuItem32W(hMenu, -1, MF_BYPOSITION, &mii);
1649     } while (!(resinfo & MF_END));
1650     return res;
1651 }
1652
1653
1654 /***********************************************************************
1655  *           MENU_GetSubPopup
1656  *
1657  * Return the handle of the selected sub-popup menu (if any).
1658  */
1659 static HMENU32 MENU_GetSubPopup( HMENU32 hmenu )
1660 {
1661     POPUPMENU *menu;
1662     MENUITEM *item;
1663
1664     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
1665
1666     if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
1667
1668     item = &menu->items[menu->FocusedItem];
1669     if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1670         return item->hSubMenu;
1671     return 0;
1672 }
1673
1674
1675 /***********************************************************************
1676  *           MENU_HideSubPopups
1677  *
1678  * Hide the sub-popup menus of this menu.
1679  */
1680 static void MENU_HideSubPopups( HWND32 hwndOwner, HMENU32 hmenu,
1681                                 BOOL32 sendMenuSelect )
1682 {
1683     POPUPMENU *menu = (POPUPMENU*) USER_HEAP_LIN_ADDR( hmenu );;
1684
1685     if (menu && uSubPWndLevel)
1686     {
1687         HMENU32 hsubmenu;
1688         POPUPMENU *submenu;
1689         MENUITEM *item;
1690
1691         if (menu->FocusedItem != NO_SELECTED_ITEM)
1692         {
1693             item = &menu->items[menu->FocusedItem];
1694             if (!(item->fType & MF_POPUP) ||
1695                 !(item->fState & MF_MOUSESELECT)) return;
1696             item->fState &= ~MF_MOUSESELECT;
1697             hsubmenu = item->hSubMenu;
1698         } else return;
1699
1700         submenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hsubmenu );
1701         MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
1702         MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect );
1703
1704         if (submenu->hWnd == pTopPopupWnd->hwndSelf ) 
1705         {
1706             ShowWindow32( submenu->hWnd, SW_HIDE );
1707             uSubPWndLevel = 0;
1708         }
1709         else
1710         {
1711             DestroyWindow32( submenu->hWnd );
1712             submenu->hWnd = 0;
1713         }
1714     }
1715 }
1716
1717
1718 /***********************************************************************
1719  *           MENU_ShowSubPopup
1720  *
1721  * Display the sub-menu of the selected item of this menu.
1722  * Return the handle of the submenu, or hmenu if no submenu to display.
1723  */
1724 static HMENU32 MENU_ShowSubPopup( HWND32 hwndOwner, HMENU32 hmenu,
1725                                   BOOL32 selectFirst )
1726 {
1727     RECT32 rect;
1728     POPUPMENU *menu;
1729     MENUITEM *item;
1730     WND *wndPtr;
1731     HDC32 hdc;
1732
1733     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return hmenu;
1734
1735     if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
1736          (menu->FocusedItem == NO_SELECTED_ITEM)) return hmenu;
1737
1738     item = &menu->items[menu->FocusedItem];
1739     if (!(item->fType & MF_POPUP) ||
1740          (item->fState & (MF_GRAYED | MF_DISABLED))) return hmenu;
1741
1742     /* message must be send before using item,
1743        because nearly everything may by changed by the application ! */
1744     rect = item->rect;
1745     SendMessage16( hwndOwner, WM_INITMENUPOPUP, (WPARAM16)item->hSubMenu,
1746                    MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
1747
1748     /* correct item if modified as a reaction to WM_INITMENUPOPUP-message */
1749     if (!(item->fState & MF_HILITE)) 
1750     {
1751         if (menu->wFlags & MF_POPUP) hdc = GetDC32( menu->hWnd );
1752         else hdc = GetDCEx32( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1753         item->fState |= MF_HILITE;
1754         MENU_DrawMenuItem( menu->hWnd, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE ); 
1755         ReleaseDC32( menu->hWnd, hdc );
1756     }
1757     if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
1758       item->rect = rect;
1759
1760     item->fState |= MF_MOUSESELECT;
1761
1762     if (IS_SYSTEM_MENU(menu))
1763     {
1764         MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, wndPtr->class->style);
1765
1766         NC_GetSysPopupPos( wndPtr, &rect );
1767         rect.top = rect.bottom;
1768         rect.right = SYSMETRICS_CXSIZE;
1769         rect.bottom = SYSMETRICS_CYSIZE;
1770     }
1771     else
1772     {
1773         if (menu->wFlags & MF_POPUP)
1774         {
1775             rect.left = wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width;
1776             rect.top = wndPtr->rectWindow.top + item->rect.top;
1777             rect.right = item->rect.left - item->rect.right + 2*arrow_bitmap_width;
1778             rect.bottom = item->rect.top - item->rect.bottom;
1779         }
1780         else
1781         {
1782             rect.left = wndPtr->rectWindow.left + item->rect.left;
1783             rect.top = wndPtr->rectWindow.top + item->rect.bottom;
1784             rect.right = item->rect.right - item->rect.left;
1785             rect.bottom = item->rect.bottom - item->rect.top;
1786         }
1787     }
1788
1789     MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
1790                     rect.left, rect.top, rect.right, rect.bottom );
1791     if (selectFirst)
1792         MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
1793     return item->hSubMenu;
1794 }
1795
1796 /***********************************************************************
1797  *           MENU_PtMenu
1798  *
1799  * Walks menu chain trying to find a menu pt maps to.
1800  */
1801 static HMENU32 MENU_PtMenu( HMENU32 hMenu, POINT16 pt )
1802 {
1803    POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hMenu );
1804    register UINT32 ht = menu->FocusedItem;
1805
1806    /* try subpopup first (if any) */
1807     ht = (ht != NO_SELECTED_ITEM &&
1808           (menu->items[ht].fType & MF_POPUP) &&
1809           (menu->items[ht].fState & MF_MOUSESELECT))
1810         ? (UINT32) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
1811
1812    if( !ht )    /* check the current window (avoiding WM_HITTEST) */
1813    {
1814         ht = (UINT32)NC_HandleNCHitTest( menu->hWnd, pt );
1815         if( menu->wFlags & MF_POPUP )
1816             ht =  (ht != (UINT32)HTNOWHERE && 
1817                    ht != (UINT32)HTERROR) ? (UINT32)hMenu : 0;
1818         else
1819         {
1820             WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
1821
1822             ht = ( ht == HTSYSMENU ) ? (UINT32)(wndPtr->hSysMenu)
1823                  : ( ht == HTMENU ) ? (UINT32)(wndPtr->wIDmenu) : 0;
1824         }
1825    }
1826    return (HMENU32)ht;
1827 }
1828
1829 /***********************************************************************
1830  *           MENU_ExecFocusedItem
1831  *
1832  * Execute a menu item (for instance when user pressed Enter).
1833  * Return TRUE if we can go on with menu tracking.
1834  */
1835 static BOOL32 MENU_ExecFocusedItem( MTRACKER* pmt, HMENU32 hMenu )
1836 {
1837     MENUITEM *item;
1838     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hMenu );
1839     if (!menu || !menu->nItems || 
1840         (menu->FocusedItem == NO_SELECTED_ITEM)) return TRUE;
1841
1842     item = &menu->items[menu->FocusedItem];
1843
1844     TRACE(menu, "%08x %08x %08x\n",
1845                  hMenu, item->wID, item->hSubMenu);
1846
1847     if (!(item->fType & MF_POPUP))
1848     {
1849         if (!(item->fState & (MF_GRAYED | MF_DISABLED)))
1850         {
1851             if( menu->wFlags & MF_SYSMENU )
1852             {
1853                 PostMessage16( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
1854                                MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
1855             }
1856             else
1857                 PostMessage16( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
1858             return FALSE;
1859         }
1860         else return TRUE;
1861     }
1862     else
1863     {
1864         pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hMenu, TRUE );
1865         return TRUE;
1866     }
1867 }
1868
1869
1870 /***********************************************************************
1871  *           MENU_SwitchTracking
1872  *
1873  * Helper function for menu navigation routines.
1874  */
1875 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU32 hPtMenu, UINT32 id )
1876 {
1877     POPUPMENU *ptmenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hPtMenu );
1878     POPUPMENU *topmenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( pmt->hTopMenu );
1879
1880     if( pmt->hTopMenu != hPtMenu &&
1881         !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
1882     {
1883         /* both are top level menus (system and menu-bar) */
1884
1885         MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
1886         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE );
1887         pmt->hTopMenu = hPtMenu;
1888     }
1889     else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
1890     MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE );
1891 }
1892
1893
1894 /***********************************************************************
1895  *           MENU_ButtonDown
1896  *
1897  * Return TRUE if we can go on with menu tracking.
1898  */
1899 static BOOL32 MENU_ButtonDown( MTRACKER* pmt, HMENU32 hPtMenu )
1900 {
1901     if (hPtMenu)
1902     {
1903         UINT32 id = 0;
1904         POPUPMENU *ptmenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hPtMenu );
1905         MENUITEM *item;
1906
1907         if( IS_SYSTEM_MENU(ptmenu) )
1908             item = ptmenu->items;
1909         else
1910             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
1911
1912         if( item )
1913         {
1914             if( ptmenu->FocusedItem == id )
1915             {
1916                 /* nothing to do with already selected non-popup */
1917                 if( !(item->fType & MF_POPUP) ) return TRUE;
1918
1919                 if( item->fState & MF_MOUSESELECT )
1920                 {
1921                     if( ptmenu->wFlags & MF_POPUP )
1922                     {
1923                         /* hide selected subpopup */
1924
1925                         MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, TRUE );
1926                         pmt->hCurrentMenu = hPtMenu;
1927                         return TRUE;
1928                     }
1929                     return FALSE; /* shouldn't get here */
1930                 } 
1931             }
1932             else MENU_SwitchTracking( pmt, hPtMenu, id );
1933
1934             /* try to display a subpopup */
1935
1936             pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE );
1937             return TRUE;
1938         } 
1939         else WARN(menu, "\tunable to find clicked item!\n");
1940     }
1941     return FALSE;
1942 }
1943
1944 /***********************************************************************
1945  *           MENU_ButtonUp
1946  *
1947  * Return TRUE if we can go on with menu tracking.
1948  */
1949 static BOOL32 MENU_ButtonUp( MTRACKER* pmt, HMENU32 hPtMenu )
1950 {
1951     if (hPtMenu)
1952     {
1953         UINT32 id = 0;
1954         POPUPMENU *ptmenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hPtMenu );
1955         MENUITEM *item;
1956
1957         if( IS_SYSTEM_MENU(ptmenu) )
1958             item = ptmenu->items;
1959         else
1960             item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
1961
1962         if( item && (ptmenu->FocusedItem == id ))
1963         {
1964             if( !(item->fType & MF_POPUP) )
1965                 return MENU_ExecFocusedItem( pmt, hPtMenu );
1966             hPtMenu = item->hSubMenu;
1967             if( hPtMenu == pmt->hCurrentMenu )
1968             {
1969                 /* Select first item of sub-popup */    
1970
1971                 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, NO_SELECTED_ITEM, FALSE );
1972                 MENU_MoveSelection( pmt->hOwnerWnd, hPtMenu, ITEM_NEXT );
1973             }
1974             return TRUE;
1975         }
1976     }
1977     return FALSE;
1978 }
1979
1980
1981 /***********************************************************************
1982  *           MENU_MouseMove
1983  *
1984  * Return TRUE if we can go on with menu tracking.
1985  */
1986 static BOOL32 MENU_MouseMove( MTRACKER* pmt, HMENU32 hPtMenu )
1987 {
1988     UINT32 id = NO_SELECTED_ITEM;
1989     POPUPMENU *ptmenu = NULL;
1990
1991     if( hPtMenu )
1992     {
1993         ptmenu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hPtMenu );
1994         if( IS_SYSTEM_MENU(ptmenu) )
1995             id = 0;
1996         else
1997             MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
1998     } 
1999
2000     if( id == NO_SELECTED_ITEM )
2001     {
2002         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu, 
2003                          NO_SELECTED_ITEM, TRUE );
2004     }
2005     else if( ptmenu->FocusedItem != id )
2006     {
2007             MENU_SwitchTracking( pmt, hPtMenu, id );
2008             pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE );
2009     }
2010     return TRUE;
2011 }
2012
2013
2014 /***********************************************************************
2015  *           MENU_DoNextMenu
2016  *
2017  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2018  */
2019 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT32 vk )
2020 {
2021     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( pmt->hTopMenu );
2022
2023     if( (vk == VK_LEFT &&  menu->FocusedItem == 0 ) ||
2024         (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2025     {
2026         WND*    wndPtr;
2027         HMENU32 hNewMenu;
2028         HWND32  hNewWnd;
2029         UINT32  id = 0;
2030         LRESULT l = SendMessage16( pmt->hOwnerWnd, WM_NEXTMENU, (WPARAM16)vk, 
2031                 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2032
2033         TRACE(menu,"%04x [%04x] -> %04x [%04x]\n",
2034                      (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2035
2036         if( l == 0 )
2037         {
2038             wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2039
2040             hNewWnd = pmt->hOwnerWnd;
2041             if( IS_SYSTEM_MENU(menu) )
2042             {
2043                 /* switch to the menu bar */
2044
2045                 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu ) 
2046                     return FALSE;
2047
2048                 hNewMenu = wndPtr->wIDmenu;
2049                 if( vk == VK_LEFT )
2050                 {
2051                     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hNewMenu );
2052                     id = menu->nItems - 1;
2053                 }
2054             }
2055             else if( wndPtr->dwStyle & WS_SYSMENU ) 
2056             {
2057                 /* switch to the system menu */
2058                 hNewMenu = wndPtr->hSysMenu; 
2059             }
2060             else return FALSE;
2061         }
2062         else    /* application returned a new menu to switch to */
2063         {
2064             hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2065
2066             if( IsMenu32(hNewMenu) && IsWindow32(hNewWnd) )
2067             {
2068                 wndPtr = WIN_FindWndPtr(hNewWnd);
2069
2070                 if( wndPtr->dwStyle & WS_SYSMENU &&
2071                     GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2072                 {
2073                     /* get the real system menu */
2074                     hNewMenu =  wndPtr->hSysMenu;
2075                 }
2076                 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2077                 {
2078                     /* FIXME: Not sure what to do here, perhaps,
2079                      * try to track hNewMenu as a popup? */
2080
2081                     TRACE(menu," -- got confused.\n");
2082                     return FALSE;
2083                 }
2084             }
2085             else return FALSE;
2086         }
2087
2088         if( hNewMenu != pmt->hTopMenu )
2089         {
2090             MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE );
2091             if( pmt->hCurrentMenu != pmt->hTopMenu ) 
2092                 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2093         }
2094
2095         if( hNewWnd != pmt->hOwnerWnd )
2096         {
2097             ReleaseCapture(); 
2098             pmt->hOwnerWnd = hNewWnd;
2099             EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2100         }
2101
2102         pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2103         MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE ); 
2104
2105         return TRUE;
2106     }
2107     return FALSE;
2108 }
2109
2110 /***********************************************************************
2111  *           MENU_SuspendPopup
2112  *
2113  * The idea is not to show the popup if the next input message is
2114  * going to hide it anyway.
2115  */
2116 static BOOL32 MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2117 {
2118     MSG16 msg;
2119
2120     msg.hwnd = pmt->hOwnerWnd;
2121
2122     PeekMessage16( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2123     pmt->trackFlags |= TF_SKIPREMOVE;
2124
2125     switch( uMsg )
2126     {
2127         case WM_KEYDOWN:
2128              PeekMessage16( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2129              if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2130              {
2131                  PeekMessage16( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2132                  PeekMessage16( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2133                  if( msg.message == WM_KEYDOWN &&
2134                     (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2135                  {
2136                      pmt->trackFlags |= TF_SUSPENDPOPUP;
2137                      return TRUE;
2138                  }
2139              }
2140              break;
2141     }
2142
2143     /* failures go through this */
2144     pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2145     return FALSE;
2146 }
2147
2148 /***********************************************************************
2149  *           MENU_KeyLeft
2150  *
2151  * Handle a VK_LEFT key event in a menu. 
2152  */
2153 static void MENU_KeyLeft( MTRACKER* pmt )
2154 {
2155     POPUPMENU *menu;
2156     HMENU32 hmenutmp, hmenuprev;
2157     UINT32  prevcol;
2158
2159     hmenuprev = hmenutmp = pmt->hTopMenu;
2160     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenutmp );
2161
2162     /* Try to move 1 column left (if possible) */
2163     if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) != 
2164         NO_SELECTED_ITEM ) {
2165         
2166         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2167                          prevcol, TRUE );
2168         return;
2169     }
2170
2171     /* close topmost popup */
2172     while (hmenutmp != pmt->hCurrentMenu)
2173     {
2174         hmenuprev = hmenutmp;
2175         hmenutmp = MENU_GetSubPopup( hmenuprev );
2176     }
2177
2178     MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2179     pmt->hCurrentMenu = hmenuprev; 
2180
2181     if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2182     {
2183         /* move menu bar selection if no more popups are left */
2184
2185         if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2186              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2187
2188         if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2189         {
2190            /* A sublevel menu was displayed - display the next one
2191             * unless there is another displacement coming up */
2192
2193             if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2194                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd,
2195                                                 pmt->hTopMenu, TRUE );
2196         }
2197     }
2198 }
2199
2200
2201 /***********************************************************************
2202  *           MENU_KeyRight
2203  *
2204  * Handle a VK_RIGHT key event in a menu.
2205  */
2206 static void MENU_KeyRight( MTRACKER* pmt )
2207 {
2208     HMENU32 hmenutmp;
2209     POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( pmt->hTopMenu );
2210     UINT32  nextcol;
2211
2212     TRACE(menu, "MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2213                   pmt->hCurrentMenu,
2214                   ((POPUPMENU *)USER_HEAP_LIN_ADDR(pmt->hCurrentMenu))->
2215                      items[0].text,
2216                   pmt->hTopMenu, menu->items[0].text );
2217
2218     if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2219     {
2220         /* If already displaying a popup, try to display sub-popup */
2221
2222         hmenutmp = pmt->hCurrentMenu;
2223         pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hmenutmp, TRUE );
2224
2225         /* if subpopup was displayed then we are done */
2226         if (hmenutmp != pmt->hCurrentMenu) return;
2227     }
2228
2229     /* Check to see if there's another column */
2230     if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) != 
2231         NO_SELECTED_ITEM ) {
2232         TRACE(menu, "Going to %d.\n", nextcol );
2233         MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2234                          nextcol, TRUE );
2235         return;
2236     }
2237
2238     if (!(menu->wFlags & MF_POPUP))     /* menu bar tracking */
2239     {
2240         if( pmt->hCurrentMenu != pmt->hTopMenu )
2241         {
2242             MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2243             hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2244         } else hmenutmp = 0;
2245
2246         /* try to move to the next item */
2247         if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2248              MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2249
2250         if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2251             if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2252                 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, 
2253                                                        pmt->hTopMenu, TRUE );
2254     }
2255 }
2256
2257
2258 /***********************************************************************
2259  *           MENU_TrackMenu
2260  *
2261  * Menu tracking code.
2262  */
2263 static BOOL32 MENU_TrackMenu( HMENU32 hmenu, UINT32 wFlags, INT32 x, INT32 y,
2264                               HWND32 hwnd, const RECT32 *lprect )
2265 {
2266     MSG16 msg;
2267     POPUPMENU *menu;
2268     BOOL32 fRemove;
2269     MTRACKER mt = { 0, hmenu, hmenu, hwnd, {x, y} };    /* control struct */
2270
2271     fEndMenu = FALSE;
2272     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu ))) return FALSE;
2273
2274     if (wFlags & TPM_BUTTONDOWN) MENU_ButtonDown( &mt, hmenu );
2275
2276     EVENT_Capture( mt.hOwnerWnd, HTMENU );
2277
2278     while (!fEndMenu)
2279     {
2280         menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( mt.hCurrentMenu );
2281         msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2282
2283         /* we have to keep the message in the queue until it's
2284          * clear that menu loop is not over yet. */
2285
2286         if (!MSG_InternalGetMessage( &msg, msg.hwnd, mt.hOwnerWnd,
2287                                      MSGF_MENU, PM_NOREMOVE, TRUE )) break;
2288
2289         TranslateMessage16( &msg );
2290         CONV_POINT16TO32( &msg.pt, &mt.pt );
2291
2292         fRemove = FALSE;
2293         if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2294         {
2295             /* Find a menu for this mouse event */
2296
2297             hmenu = MENU_PtMenu( mt.hTopMenu, msg.pt );
2298
2299             switch(msg.message)
2300             {
2301                 /* no WM_NC... messages in captured state */
2302
2303                 case WM_RBUTTONDBLCLK:
2304                 case WM_RBUTTONDOWN:
2305                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2306                     /* fall through */
2307                 case WM_LBUTTONDBLCLK:
2308                 case WM_LBUTTONDOWN:
2309                     fEndMenu |= !MENU_ButtonDown( &mt, hmenu );
2310                     break;
2311                 
2312                 case WM_RBUTTONUP:
2313                     if (!(wFlags & TPM_RIGHTBUTTON)) break;
2314                     /* fall through */
2315                 case WM_LBUTTONUP:
2316                     /* If outside all menus but inside lprect, ignore it */
2317                     if (hmenu || !lprect || !PtInRect32(lprect, mt.pt))
2318                     {
2319                         fEndMenu |= !MENU_ButtonUp( &mt, hmenu );
2320                         fRemove = TRUE; 
2321                     }
2322                     break;
2323                 
2324                 case WM_MOUSEMOVE:
2325                     if ((msg.wParam & MK_LBUTTON) || ((wFlags & TPM_RIGHTBUTTON) 
2326                                                   && (msg.wParam & MK_RBUTTON)))
2327                     {
2328                         fEndMenu |= !MENU_MouseMove( &mt, hmenu );
2329                     }
2330             } /* switch(msg.message) - mouse */
2331         }
2332         else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2333         {
2334             fRemove = TRUE;  /* Keyboard messages are always removed */
2335             switch(msg.message)
2336             {
2337             case WM_KEYDOWN:
2338                 switch(msg.wParam)
2339                 {
2340                 case VK_HOME:
2341                 case VK_END:
2342                     MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, 
2343                                      NO_SELECTED_ITEM, FALSE );
2344                 /* fall through */
2345                 case VK_UP:
2346                     MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
2347                                        (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2348                     break;
2349
2350                 case VK_DOWN: /* If on menu bar, pull-down the menu */
2351
2352                     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( mt.hCurrentMenu );
2353                     if (!(menu->wFlags & MF_POPUP))
2354                         mt.hCurrentMenu = MENU_ShowSubPopup( mt.hOwnerWnd, mt.hTopMenu, TRUE );
2355                     else      /* otherwise try to move selection */
2356                         MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2357                     break;
2358
2359                 case VK_LEFT:
2360                     MENU_KeyLeft( &mt );
2361                     break;
2362                     
2363                 case VK_RIGHT:
2364                     MENU_KeyRight( &mt );
2365                     break;
2366                     
2367                 case VK_SPACE:
2368                 case VK_RETURN:
2369                     fEndMenu |= !MENU_ExecFocusedItem( &mt, mt.hCurrentMenu );
2370                     break;
2371
2372                 case VK_ESCAPE:
2373                     fEndMenu = TRUE;
2374                     break;
2375
2376                 default:
2377                     break;
2378                 }
2379                 break;  /* WM_KEYDOWN */
2380
2381             case WM_SYSKEYDOWN:
2382                 switch(msg.wParam)
2383                 {
2384                 case VK_MENU:
2385                     fEndMenu = TRUE;
2386                     break;
2387                     
2388                 }
2389                 break;  /* WM_SYSKEYDOWN */
2390
2391             case WM_CHAR:
2392                 {
2393                     UINT32      pos;
2394
2395                       /* Hack to avoid control chars. */
2396                       /* We will find a better way real soon... */
2397                     if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2398
2399                     pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu, 
2400                                                         msg.wParam, FALSE );
2401                     if (pos == (UINT32)-2) fEndMenu = TRUE;
2402                     else if (pos == (UINT32)-1) MessageBeep32(0);
2403                     else
2404                     {
2405                         MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos, TRUE );
2406                         fEndMenu |= !MENU_ExecFocusedItem( &mt, mt.hCurrentMenu );
2407                     }
2408                 }                   
2409                 break;
2410             }  /* switch(msg.message) - kbd */
2411         }
2412         else
2413         {
2414             DispatchMessage16( &msg );
2415         }
2416
2417         if (!fEndMenu) fRemove = TRUE;
2418
2419         /* finally remove message from the queue */
2420
2421         if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2422             PeekMessage16( &msg, 0, msg.message, msg.message, PM_REMOVE );
2423         else mt.trackFlags &= ~TF_SKIPREMOVE;
2424     }
2425
2426     ReleaseCapture();
2427     if( IsWindow32( mt.hOwnerWnd ) )
2428     {
2429         MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2430
2431         menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( mt.hTopMenu );
2432         if (menu && menu->wFlags & MF_POPUP) 
2433         {
2434             ShowWindow32( menu->hWnd, SW_HIDE );
2435             uSubPWndLevel = 0;
2436         }
2437         MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE );
2438         SendMessage16( mt.hOwnerWnd, WM_MENUSELECT, 0, MAKELONG( 0xffff, 0 ) );
2439     }
2440     fEndMenu = FALSE;
2441     return TRUE;
2442 }
2443
2444 /***********************************************************************
2445  *           MENU_InitTracking
2446  */
2447 static BOOL32 MENU_InitTracking(HWND32 hWnd, HMENU32 hMenu)
2448 {
2449     HideCaret32(0);
2450     SendMessage16( hWnd, WM_ENTERMENULOOP, 0, 0 );
2451     SendMessage16( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
2452     SendMessage16( hWnd, WM_INITMENU, hMenu, 0 );
2453     return TRUE;
2454 }
2455
2456 /***********************************************************************
2457  *           MENU_TrackMouseMenuBar
2458  *
2459  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2460  */
2461 void MENU_TrackMouseMenuBar( WND* wndPtr, INT32 ht, POINT32 pt )
2462 {
2463     HWND32  hWnd = wndPtr->hwndSelf;
2464     HMENU32 hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
2465
2466     if (IsMenu32(hMenu))
2467     {
2468         MENU_InitTracking( hWnd, hMenu );
2469         MENU_TrackMenu( hMenu, TPM_ENTERIDLEEX | TPM_BUTTONDOWN | 
2470                         TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, hWnd, NULL );
2471
2472         SendMessage16( hWnd, WM_EXITMENULOOP, 0, 0 );
2473         ShowCaret32(0);
2474     }
2475 }
2476
2477
2478 /***********************************************************************
2479  *           MENU_TrackKbdMenuBar
2480  *
2481  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
2482  */
2483 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT32 wParam, INT32 vkey)
2484 {
2485    UINT32 uItem = NO_SELECTED_ITEM;
2486    HMENU32 hTrackMenu; 
2487
2488     /* find window that has a menu */
2489  
2490     while( wndPtr->dwStyle & WS_CHILD && !(wndPtr->dwStyle & WS_SYSMENU) )
2491         if( !(wndPtr = wndPtr->parent) ) return;
2492
2493     /* check if we have to track a system menu */
2494           
2495     if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) || 
2496         !wndPtr->wIDmenu || vkey == VK_SPACE )
2497     {
2498         if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
2499         hTrackMenu = wndPtr->hSysMenu;
2500         uItem = 0;
2501         wParam |= HTSYSMENU;    /* prevent item lookup */
2502     }
2503     else
2504         hTrackMenu = wndPtr->wIDmenu;
2505
2506     if (IsMenu32( hTrackMenu ))
2507     {
2508         MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu );
2509
2510         if( vkey && vkey != VK_SPACE )
2511         {
2512             uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu, 
2513                                         vkey, (wParam & HTSYSMENU) );
2514             if( uItem >= (UINT32)(-2) )
2515             {
2516                 if( uItem == (UINT32)(-1) ) MessageBeep32(0);
2517                 hTrackMenu = 0;
2518             }
2519         }
2520
2521         if( hTrackMenu )
2522         {
2523             MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE );
2524
2525             if( uItem == NO_SELECTED_ITEM )
2526                 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
2527             else if( vkey )
2528                 PostMessage16( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
2529
2530             MENU_TrackMenu( hTrackMenu, TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON,
2531                             0, 0, wndPtr->hwndSelf, NULL );
2532         }
2533         SendMessage16( wndPtr->hwndSelf, WM_EXITMENULOOP, 0, 0 );
2534         ShowCaret32(0);
2535     }
2536 }
2537
2538
2539 /**********************************************************************
2540  *           TrackPopupMenu16   (USER.416)
2541  */
2542 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
2543                            INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
2544 {
2545     RECT32 r;
2546     if (lpRect)
2547          CONV_RECT16TO32( lpRect, &r );
2548     return TrackPopupMenu32( hMenu, wFlags, x, y, nReserved, hWnd,
2549                              lpRect ? &r : NULL );
2550 }
2551
2552
2553 /**********************************************************************
2554  *           TrackPopupMenu32   (USER32.549)
2555  */
2556 BOOL32 WINAPI TrackPopupMenu32( HMENU32 hMenu, UINT32 wFlags, INT32 x, INT32 y,
2557                            INT32 nReserved, HWND32 hWnd, const RECT32 *lpRect )
2558 {
2559     BOOL32 ret = FALSE;
2560
2561     HideCaret32(0);
2562     SendMessage16( hWnd, WM_INITMENUPOPUP, (WPARAM16)hMenu, 0);
2563     if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 )) 
2564         ret = MENU_TrackMenu( hMenu, wFlags & ~TPM_INTERNAL, 0, 0, hWnd, lpRect );
2565     ShowCaret32(0);
2566     return ret;
2567 }
2568
2569 /**********************************************************************
2570  *           TrackPopupMenuEx   (USER32.550)
2571  */
2572 BOOL32 WINAPI TrackPopupMenuEx( HMENU32 hMenu, UINT32 wFlags, INT32 x, INT32 y,
2573                                 HWND32 hWnd, LPTPMPARAMS lpTpm )
2574 {
2575     FIXME(menu, "not fully implemented\n" );
2576     return TrackPopupMenu32( hMenu, wFlags, x, y, 0, hWnd,
2577                              lpTpm ? &lpTpm->rcExclude : NULL );
2578 }
2579
2580 /***********************************************************************
2581  *           PopupMenuWndProc
2582  *
2583  * NOTE: Windows has totally different (and undocumented) popup wndproc.
2584  */
2585 LRESULT WINAPI PopupMenuWndProc( HWND32 hwnd, UINT32 message, WPARAM32 wParam,
2586                                  LPARAM lParam )
2587 {    
2588     WND* wndPtr = WIN_FindWndPtr(hwnd);
2589
2590     switch(message)
2591     {
2592     case WM_CREATE:
2593         {
2594             CREATESTRUCT32A *cs = (CREATESTRUCT32A*)lParam;
2595             SetWindowLong32A( hwnd, 0, (LONG)cs->lpCreateParams );
2596             return 0;
2597         }
2598
2599     case WM_MOUSEACTIVATE:  /* We don't want to be activated */
2600         return MA_NOACTIVATE;
2601
2602     case WM_PAINT:
2603         {
2604             PAINTSTRUCT32 ps;
2605             BeginPaint32( hwnd, &ps );
2606             MENU_DrawPopupMenu( hwnd, ps.hdc,
2607                                 (HMENU32)GetWindowLong32A( hwnd, 0 ) );
2608             EndPaint32( hwnd, &ps );
2609             return 0;
2610         }
2611     case WM_ERASEBKGND:
2612         return 1;
2613
2614     case WM_DESTROY:
2615
2616         /* zero out global pointer in case resident popup window
2617          * was somehow destroyed. */
2618
2619         if( pTopPopupWnd )
2620         {
2621             if( hwnd == pTopPopupWnd->hwndSelf )
2622             {
2623                 ERR(menu, "resident popup destroyed!\n");
2624
2625                 pTopPopupWnd = NULL;
2626                 uSubPWndLevel = 0;
2627             }
2628             else
2629                 uSubPWndLevel--;
2630         }
2631         break;
2632
2633     case WM_SHOWWINDOW:
2634
2635         if( wParam )
2636         {
2637             if( !(*(HMENU32*)wndPtr->wExtra) )
2638                 ERR(menu,"no menu to display\n");
2639         }
2640         else
2641             *(HMENU32*)wndPtr->wExtra = 0;
2642         break;
2643
2644     case MM_SETMENUHANDLE:
2645
2646         *(HMENU32*)wndPtr->wExtra = (HMENU32)wParam;
2647         break;
2648
2649     case MM_GETMENUHANDLE:
2650
2651         return *(HMENU32*)wndPtr->wExtra;
2652
2653     default:
2654         return DefWindowProc32A( hwnd, message, wParam, lParam );
2655     }
2656     return 0;
2657 }
2658
2659
2660 /***********************************************************************
2661  *           MENU_GetMenuBarHeight
2662  *
2663  * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
2664  */
2665 UINT32 MENU_GetMenuBarHeight( HWND32 hwnd, UINT32 menubarWidth,
2666                               INT32 orgX, INT32 orgY )
2667 {
2668     HDC32 hdc;
2669     RECT32 rectBar;
2670     WND *wndPtr;
2671     LPPOPUPMENU lppop;
2672
2673     TRACE(menu, "HWND 0x%x, width %d, "
2674                   "at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
2675     
2676     if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
2677     if (!(lppop = (LPPOPUPMENU)USER_HEAP_LIN_ADDR((HMENU16)wndPtr->wIDmenu)))
2678       return 0;
2679     hdc = GetDCEx32( hwnd, 0, DCX_CACHE | DCX_WINDOW );
2680     SetRect32(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU);
2681     MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
2682     ReleaseDC32( hwnd, hdc );
2683     return lppop->Height;
2684 }
2685
2686
2687 /*******************************************************************
2688  *         ChangeMenu16    (USER.153)
2689  */
2690 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
2691                             UINT16 id, UINT16 flags )
2692 {
2693     TRACE(menu,"menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
2694                   hMenu, pos, (DWORD)data, id, flags );
2695     if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
2696                                                 id, data );
2697
2698     /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
2699     /* for MF_DELETE. We should check the parameters for all others */
2700     /* MF_* actions also (anybody got a doc on ChangeMenu?). */
2701
2702     if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
2703     if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
2704                                                id, data );
2705     if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
2706                                               flags & MF_BYPOSITION ? pos : id,
2707                                               flags & ~MF_REMOVE );
2708     /* Default: MF_INSERT */
2709     return InsertMenu16( hMenu, pos, flags, id, data );
2710 }
2711
2712
2713 /*******************************************************************
2714  *         ChangeMenu32A    (USER32.23)
2715  */
2716 BOOL32 WINAPI ChangeMenu32A( HMENU32 hMenu, UINT32 pos, LPCSTR data,
2717                              UINT32 id, UINT32 flags )
2718 {
2719     TRACE(menu,"menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
2720                   hMenu, pos, (DWORD)data, id, flags );
2721     if (flags & MF_APPEND) return AppendMenu32A( hMenu, flags & ~MF_APPEND,
2722                                                  id, data );
2723     if (flags & MF_DELETE) return DeleteMenu32(hMenu, pos, flags & ~MF_DELETE);
2724     if (flags & MF_CHANGE) return ModifyMenu32A(hMenu, pos, flags & ~MF_CHANGE,
2725                                                 id, data );
2726     if (flags & MF_REMOVE) return RemoveMenu32( hMenu,
2727                                               flags & MF_BYPOSITION ? pos : id,
2728                                               flags & ~MF_REMOVE );
2729     /* Default: MF_INSERT */
2730     return InsertMenu32A( hMenu, pos, flags, id, data );
2731 }
2732
2733
2734 /*******************************************************************
2735  *         ChangeMenu32W    (USER32.24)
2736  */
2737 BOOL32 WINAPI ChangeMenu32W( HMENU32 hMenu, UINT32 pos, LPCWSTR data,
2738                              UINT32 id, UINT32 flags )
2739 {
2740     TRACE(menu,"menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
2741                   hMenu, pos, (DWORD)data, id, flags );
2742     if (flags & MF_APPEND) return AppendMenu32W( hMenu, flags & ~MF_APPEND,
2743                                                  id, data );
2744     if (flags & MF_DELETE) return DeleteMenu32(hMenu, pos, flags & ~MF_DELETE);
2745     if (flags & MF_CHANGE) return ModifyMenu32W(hMenu, pos, flags & ~MF_CHANGE,
2746                                                 id, data );
2747     if (flags & MF_REMOVE) return RemoveMenu32( hMenu,
2748                                               flags & MF_BYPOSITION ? pos : id,
2749                                               flags & ~MF_REMOVE );
2750     /* Default: MF_INSERT */
2751     return InsertMenu32W( hMenu, pos, flags, id, data );
2752 }
2753
2754
2755 /*******************************************************************
2756  *         CheckMenuItem16    (USER.154)
2757  */
2758 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
2759 {
2760     return (BOOL16)CheckMenuItem32( hMenu, id, flags );
2761 }
2762
2763
2764 /*******************************************************************
2765  *         CheckMenuItem32    (USER32.46)
2766  */
2767 DWORD WINAPI CheckMenuItem32( HMENU32 hMenu, UINT32 id, UINT32 flags )
2768 {
2769     MENUITEM *item;
2770     DWORD ret;
2771
2772     TRACE(menu,"%04x %04x %04x\n", hMenu, id, flags );
2773     if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
2774     ret = item->fState & MF_CHECKED;
2775     if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
2776     else item->fState &= ~MF_CHECKED;
2777     return ret;
2778 }
2779
2780
2781 /**********************************************************************
2782  *         EnableMenuItem16    (USER.155)
2783  */
2784 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
2785 {
2786     return EnableMenuItem32( hMenu, wItemID, wFlags );
2787 }
2788
2789
2790 /**********************************************************************
2791  *         EnableMenuItem32    (USER32.170)
2792  */
2793 UINT32 WINAPI EnableMenuItem32( HMENU32 hMenu, UINT32 wItemID, UINT32 wFlags )
2794 {
2795     UINT32    oldflags;
2796     MENUITEM *item;
2797
2798     TRACE(menu,"(%04x, %04X, %04X) !\n", 
2799                  hMenu, wItemID, wFlags);
2800
2801     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
2802         return (UINT32)-1;
2803
2804     oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
2805     item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
2806     return oldflags;
2807 }
2808
2809
2810 /*******************************************************************
2811  *         GetMenuString16    (USER.161)
2812  */
2813 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
2814                               LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
2815 {
2816     return GetMenuString32A( hMenu, wItemID, str, nMaxSiz, wFlags );
2817 }
2818
2819
2820 /*******************************************************************
2821  *         GetMenuString32A    (USER32.268)
2822  */
2823 INT32 WINAPI GetMenuString32A( HMENU32 hMenu, UINT32 wItemID,
2824                                LPSTR str, INT32 nMaxSiz, UINT32 wFlags )
2825 {
2826     MENUITEM *item;
2827
2828     TRACE(menu, "menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
2829                  hMenu, wItemID, str, nMaxSiz, wFlags );
2830     if (!str || !nMaxSiz) return 0;
2831     str[0] = '\0';
2832     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
2833     if (!IS_STRING_ITEM(item->fType)) return 0;
2834     lstrcpyn32A( str, item->text, nMaxSiz );
2835     TRACE(menu, "returning '%s'\n", str );
2836     return strlen(str);
2837 }
2838
2839
2840 /*******************************************************************
2841  *         GetMenuString32W    (USER32.269)
2842  */
2843 INT32 WINAPI GetMenuString32W( HMENU32 hMenu, UINT32 wItemID,
2844                                LPWSTR str, INT32 nMaxSiz, UINT32 wFlags )
2845 {
2846     MENUITEM *item;
2847
2848     TRACE(menu, "menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
2849                  hMenu, wItemID, str, nMaxSiz, wFlags );
2850     if (!str || !nMaxSiz) return 0;
2851     str[0] = '\0';
2852     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
2853     if (!IS_STRING_ITEM(item->fType)) return 0;
2854     lstrcpynAtoW( str, item->text, nMaxSiz );
2855     return lstrlen32W(str);
2856 }
2857
2858
2859 /**********************************************************************
2860  *         HiliteMenuItem16    (USER.162)
2861  */
2862 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
2863                                 UINT16 wHilite )
2864 {
2865     return HiliteMenuItem32( hWnd, hMenu, wItemID, wHilite );
2866 }
2867
2868
2869 /**********************************************************************
2870  *         HiliteMenuItem32    (USER32.318)
2871  */
2872 BOOL32 WINAPI HiliteMenuItem32( HWND32 hWnd, HMENU32 hMenu, UINT32 wItemID,
2873                                 UINT32 wHilite )
2874 {
2875     LPPOPUPMENU menu;
2876     TRACE(menu,"(%04x, %04x, %04x, %04x);\n", 
2877                  hWnd, hMenu, wItemID, wHilite);
2878     if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
2879     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
2880     if (menu->FocusedItem == wItemID) return TRUE;
2881     MENU_HideSubPopups( hWnd, hMenu, FALSE );
2882     MENU_SelectItem( hWnd, hMenu, wItemID, TRUE );
2883     return TRUE;
2884 }
2885
2886
2887 /**********************************************************************
2888  *         GetMenuState16    (USER.250)
2889  */
2890 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
2891 {
2892     return GetMenuState32( hMenu, wItemID, wFlags );
2893 }
2894
2895
2896 /**********************************************************************
2897  *         GetMenuState32    (USER32.267)
2898  */
2899 UINT32 WINAPI GetMenuState32( HMENU32 hMenu, UINT32 wItemID, UINT32 wFlags )
2900 {
2901     MENUITEM *item;
2902     TRACE(menu,"(%04x, %04x, %04x);\n", 
2903                  hMenu, wItemID, wFlags);
2904     if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
2905     debug_print_menuitem ("  item: ", item, "");
2906     if (item->fType & MF_POPUP)
2907     {
2908         POPUPMENU *menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( item->hSubMenu );
2909         if (!menu) return -1;
2910         else return (menu->nItems << 8) | (menu->wFlags & 0xff);
2911     }
2912     else
2913     {
2914          /* We used to (from way back then) mask the result to 0xff.  */
2915          /* I don't know why and it seems wrong as the documented */
2916          /* return flag MF_SEPARATOR is outside that mask.  */
2917          return (item->fType | item->fState);
2918     }
2919 }
2920
2921
2922 /**********************************************************************
2923  *         GetMenuItemCount16    (USER.263)
2924  */
2925 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
2926 {
2927     LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2928     if (!IS_A_MENU(menu)) return -1;
2929     TRACE(menu,"(%04x) returning %d\n", 
2930                   hMenu, menu->nItems );
2931     return menu->nItems;
2932 }
2933
2934
2935 /**********************************************************************
2936  *         GetMenuItemCount32    (USER32.262)
2937  */
2938 INT32 WINAPI GetMenuItemCount32( HMENU32 hMenu )
2939 {
2940     LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
2941     if (!IS_A_MENU(menu)) return -1;
2942     TRACE(menu,"(%04x) returning %d\n", 
2943                   hMenu, menu->nItems );
2944     return menu->nItems;
2945 }
2946
2947
2948 /**********************************************************************
2949  *         GetMenuItemID16    (USER.264)
2950  */
2951 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
2952 {
2953     LPPOPUPMENU menu;
2954
2955     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
2956     if ((nPos < 0) || ((UINT16) nPos >= menu->nItems)) return -1;
2957     if (menu->items[nPos].fType & MF_POPUP) return -1;
2958     return menu->items[nPos].wID;
2959 }
2960
2961
2962 /**********************************************************************
2963  *         GetMenuItemID32    (USER32.263)
2964  */
2965 UINT32 WINAPI GetMenuItemID32( HMENU32 hMenu, INT32 nPos )
2966 {
2967     LPPOPUPMENU menu;
2968
2969     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return -1;
2970     if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
2971     return menu->items[nPos].wID;
2972 }
2973
2974
2975 /*******************************************************************
2976  *         InsertMenu16    (USER.410)
2977  */
2978 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
2979                             UINT16 id, SEGPTR data )
2980 {
2981     UINT32 pos32 = (UINT32)pos;
2982     if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT32)-1;
2983     if (IS_STRING_ITEM(flags) && data)
2984         return InsertMenu32A( hMenu, pos32, flags, id,
2985                               (LPSTR)PTR_SEG_TO_LIN(data) );
2986     return InsertMenu32A( hMenu, pos32, flags, id, (LPSTR)data );
2987 }
2988
2989
2990 /*******************************************************************
2991  *         InsertMenu32A    (USER32.322)
2992  */
2993 BOOL32 WINAPI InsertMenu32A( HMENU32 hMenu, UINT32 pos, UINT32 flags,
2994                              UINT32 id, LPCSTR str )
2995 {
2996     MENUITEM *item;
2997
2998     if (IS_STRING_ITEM(flags) && str)
2999         TRACE(menu, "hMenu %04x, pos %d, flags %08x, "
3000                       "id %04x, str '%s'\n",
3001                       hMenu, pos, flags, id, str );
3002     else TRACE(menu, "hMenu %04x, pos %d, flags %08x, "
3003                        "id %04x, str %08lx (not a string)\n",
3004                        hMenu, pos, flags, id, (DWORD)str );
3005
3006     if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3007
3008     if (!(MENU_SetItemData( item, flags, id, str )))
3009     {
3010         RemoveMenu32( hMenu, pos, flags );
3011         return FALSE;
3012     }
3013
3014     if (flags & MF_POPUP)  /* Set the MF_POPUP flag on the popup-menu */
3015         ((POPUPMENU *)USER_HEAP_LIN_ADDR((HMENU16)id))->wFlags |= MF_POPUP;
3016
3017     item->hCheckBit = item->hUnCheckBit = 0;
3018     return TRUE;
3019 }
3020
3021
3022 /*******************************************************************
3023  *         InsertMenu32W    (USER32.325)
3024  */
3025 BOOL32 WINAPI InsertMenu32W( HMENU32 hMenu, UINT32 pos, UINT32 flags,
3026                              UINT32 id, LPCWSTR str )
3027 {
3028     BOOL32 ret;
3029
3030     if (IS_STRING_ITEM(flags) && str)
3031     {
3032         LPSTR newstr = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
3033         ret = InsertMenu32A( hMenu, pos, flags, id, newstr );
3034         HeapFree( GetProcessHeap(), 0, newstr );
3035         return ret;
3036     }
3037     else return InsertMenu32A( hMenu, pos, flags, id, (LPCSTR)str );
3038 }
3039
3040
3041 /*******************************************************************
3042  *         AppendMenu16    (USER.411)
3043  */
3044 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3045 {
3046     return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3047 }
3048
3049
3050 /*******************************************************************
3051  *         AppendMenu32A    (USER32.5)
3052  */
3053 BOOL32 WINAPI AppendMenu32A( HMENU32 hMenu, UINT32 flags,
3054                              UINT32 id, LPCSTR data )
3055 {
3056     return InsertMenu32A( hMenu, -1, flags | MF_BYPOSITION, id, data );
3057 }
3058
3059
3060 /*******************************************************************
3061  *         AppendMenu32W    (USER32.6)
3062  */
3063 BOOL32 WINAPI AppendMenu32W( HMENU32 hMenu, UINT32 flags,
3064                              UINT32 id, LPCWSTR data )
3065 {
3066     return InsertMenu32W( hMenu, -1, flags | MF_BYPOSITION, id, data );
3067 }
3068
3069
3070 /**********************************************************************
3071  *         RemoveMenu16    (USER.412)
3072  */
3073 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3074 {
3075     return RemoveMenu32( hMenu, nPos, wFlags );
3076 }
3077
3078
3079 /**********************************************************************
3080  *         RemoveMenu32    (USER32.441)
3081  */
3082 BOOL32 WINAPI RemoveMenu32( HMENU32 hMenu, UINT32 nPos, UINT32 wFlags )
3083 {
3084     LPPOPUPMENU menu;
3085     MENUITEM *item;
3086
3087     TRACE(menu,"(%04x, %04x, %04x)\n",hMenu, nPos, wFlags);
3088     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3089     if (!(menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
3090     
3091       /* Remove item */
3092
3093     MENU_FreeItemData( item );
3094
3095     if (--menu->nItems == 0)
3096     {
3097         HeapFree( SystemHeap, 0, menu->items );
3098         menu->items = NULL;
3099     }
3100     else
3101     {
3102         while(nPos < menu->nItems)
3103         {
3104             *item = *(item+1);
3105             item++;
3106             nPos++;
3107         }
3108         menu->items = HeapReAlloc( SystemHeap, 0, menu->items,
3109                                    menu->nItems * sizeof(MENUITEM) );
3110     }
3111     return TRUE;
3112 }
3113
3114
3115 /**********************************************************************
3116  *         DeleteMenu16    (USER.413)
3117  */
3118 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3119 {
3120     return DeleteMenu32( hMenu, nPos, wFlags );
3121 }
3122
3123
3124 /**********************************************************************
3125  *         DeleteMenu32    (USER32.129)
3126  */
3127 BOOL32 WINAPI DeleteMenu32( HMENU32 hMenu, UINT32 nPos, UINT32 wFlags )
3128 {
3129     MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3130     if (!item) return FALSE;
3131     if (item->fType & MF_POPUP) DestroyMenu32( item->hSubMenu );
3132       /* nPos is now the position of the item */
3133     RemoveMenu32( hMenu, nPos, wFlags | MF_BYPOSITION );
3134     return TRUE;
3135 }
3136
3137
3138 /*******************************************************************
3139  *         ModifyMenu16    (USER.414)
3140  */
3141 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3142                             UINT16 id, SEGPTR data )
3143 {
3144     if (IS_STRING_ITEM(flags))
3145         return ModifyMenu32A( hMenu, pos, flags, id,
3146                               (LPSTR)PTR_SEG_TO_LIN(data) );
3147     return ModifyMenu32A( hMenu, pos, flags, id, (LPSTR)data );
3148 }
3149
3150
3151 /*******************************************************************
3152  *         ModifyMenu32A    (USER32.397)
3153  */
3154 BOOL32 WINAPI ModifyMenu32A( HMENU32 hMenu, UINT32 pos, UINT32 flags,
3155                              UINT32 id, LPCSTR str )
3156 {
3157     MENUITEM *item;
3158
3159     if (IS_STRING_ITEM(flags))
3160     {
3161         TRACE(menu, "%04x %d %04x %04x '%s'\n",
3162                       hMenu, pos, flags, id, str ? str : "#NULL#" );
3163         if (!str) return FALSE;
3164     }
3165     else
3166     {
3167         TRACE(menu, "%04x %d %04x %04x %08lx\n",
3168                       hMenu, pos, flags, id, (DWORD)str );
3169     }
3170
3171     if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3172     return MENU_SetItemData( item, flags, id, str );
3173 }
3174
3175
3176 /*******************************************************************
3177  *         ModifyMenu32W    (USER32.398)
3178  */
3179 BOOL32 WINAPI ModifyMenu32W( HMENU32 hMenu, UINT32 pos, UINT32 flags,
3180                              UINT32 id, LPCWSTR str )
3181 {
3182     BOOL32 ret;
3183
3184     if (IS_STRING_ITEM(flags) && str)
3185     {
3186         LPSTR newstr = HEAP_strdupWtoA( GetProcessHeap(), 0, str );
3187         ret = ModifyMenu32A( hMenu, pos, flags, id, newstr );
3188         HeapFree( GetProcessHeap(), 0, newstr );
3189         return ret;
3190     }
3191     else return ModifyMenu32A( hMenu, pos, flags, id, (LPCSTR)str );
3192 }
3193
3194
3195 /**********************************************************************
3196  *         CreatePopupMenu16    (USER.415)
3197  */
3198 HMENU16 WINAPI CreatePopupMenu16(void)
3199 {
3200     return CreatePopupMenu32();
3201 }
3202
3203
3204 /**********************************************************************
3205  *         CreatePopupMenu32    (USER32.82)
3206  */
3207 HMENU32 WINAPI CreatePopupMenu32(void)
3208 {
3209     HMENU32 hmenu;
3210     POPUPMENU *menu;
3211
3212     if (!(hmenu = CreateMenu32())) return 0;
3213     menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
3214     menu->wFlags |= MF_POPUP;
3215     return hmenu;
3216 }
3217
3218
3219 /**********************************************************************
3220  *         GetMenuCheckMarkDimensions    (USER.417) (USER32.258)
3221  */
3222 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3223 {
3224     return MAKELONG( check_bitmap_width, check_bitmap_height );
3225 }
3226
3227
3228 /**********************************************************************
3229  *         SetMenuItemBitmaps16    (USER.418)
3230  */
3231 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3232                                     HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3233 {
3234     return SetMenuItemBitmaps32( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3235 }
3236
3237
3238 /**********************************************************************
3239  *         SetMenuItemBitmaps32    (USER32.490)
3240  */
3241 BOOL32 WINAPI SetMenuItemBitmaps32( HMENU32 hMenu, UINT32 nPos, UINT32 wFlags,
3242                                     HBITMAP32 hNewUnCheck, HBITMAP32 hNewCheck)
3243 {
3244     MENUITEM *item;
3245     TRACE(menu,"(%04x, %04x, %04x, %04x, %04x)\n",
3246                  hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3247     if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3248
3249     if (!hNewCheck && !hNewUnCheck)
3250     {
3251         item->fState &= ~MF_USECHECKBITMAPS;
3252     }
3253     else  /* Install new bitmaps */
3254     {
3255         item->hCheckBit = hNewCheck;
3256         item->hUnCheckBit = hNewUnCheck;
3257         item->fState |= MF_USECHECKBITMAPS;
3258     }
3259     return TRUE;
3260 }
3261
3262
3263 /**********************************************************************
3264  *         CreateMenu16    (USER.151)
3265  */
3266 HMENU16 WINAPI CreateMenu16(void)
3267 {
3268     return CreateMenu32();
3269 }
3270
3271
3272 /**********************************************************************
3273  *         CreateMenu32    (USER32.81)
3274  */
3275 HMENU32 WINAPI CreateMenu32(void)
3276 {
3277     HMENU32 hMenu;
3278     LPPOPUPMENU menu;
3279     if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3280     menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3281     menu->wFlags = 0;
3282     menu->wMagic = MENU_MAGIC;
3283     menu->hTaskQ = 0;
3284     menu->Width  = 0;
3285     menu->Height = 0;
3286     menu->nItems = 0;
3287     menu->hWnd   = 0;
3288     menu->items  = NULL;
3289     menu->FocusedItem = NO_SELECTED_ITEM;
3290     TRACE(menu, "return %04x\n", hMenu );
3291     return hMenu;
3292 }
3293
3294
3295 /**********************************************************************
3296  *         DestroyMenu16    (USER.152)
3297  */
3298 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3299 {
3300     return DestroyMenu32( hMenu );
3301 }
3302
3303
3304 /**********************************************************************
3305  *         DestroyMenu32    (USER32.134)
3306  */
3307 BOOL32 WINAPI DestroyMenu32( HMENU32 hMenu )
3308 {
3309     TRACE(menu,"(%04x)\n", hMenu);
3310
3311     /* Silently ignore attempts to destroy default system popup */
3312
3313     if (hMenu && hMenu != MENU_DefSysPopup)
3314     {
3315         LPPOPUPMENU lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3316
3317         if( pTopPopupWnd && (hMenu == *(HMENU32*)pTopPopupWnd->wExtra) )
3318           *(UINT32*)pTopPopupWnd->wExtra = 0;
3319
3320         if (IS_A_MENU( lppop ))
3321         {
3322             lppop->wMagic = 0;  /* Mark it as destroyed */
3323
3324             if ((lppop->wFlags & MF_POPUP) && lppop->hWnd &&
3325                 (!pTopPopupWnd || (lppop->hWnd != pTopPopupWnd->hwndSelf)))
3326                 DestroyWindow32( lppop->hWnd );
3327
3328             if (lppop->items)   /* recursively destroy submenus */
3329             {
3330                 int i;
3331                 MENUITEM *item = lppop->items;
3332                 for (i = lppop->nItems; i > 0; i--, item++)
3333                 {
3334                     if (item->fType & MF_POPUP) DestroyMenu32(item->hSubMenu);
3335                     MENU_FreeItemData( item );
3336                 }
3337                 HeapFree( SystemHeap, 0, lppop->items );
3338             }
3339             USER_HEAP_FREE( hMenu );
3340         }
3341         else return FALSE;
3342     }
3343     return (hMenu != MENU_DefSysPopup);
3344 }
3345
3346
3347 /**********************************************************************
3348  *         GetSystemMenu16    (USER.156)
3349  */
3350 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3351 {
3352     return GetSystemMenu32( hWnd, bRevert );
3353 }
3354
3355
3356 /**********************************************************************
3357  *         GetSystemMenu32    (USER32.291)
3358  */
3359 HMENU32 WINAPI GetSystemMenu32( HWND32 hWnd, BOOL32 bRevert )
3360 {
3361     WND *wndPtr = WIN_FindWndPtr( hWnd );
3362
3363     if (wndPtr)
3364     {
3365         if( wndPtr->hSysMenu )
3366         {
3367             if( bRevert )
3368             {
3369                 DestroyMenu32(wndPtr->hSysMenu); 
3370                 wndPtr->hSysMenu = 0;
3371             }
3372             else
3373             {
3374                 POPUPMENU *menu = (POPUPMENU*) 
3375                            USER_HEAP_LIN_ADDR(wndPtr->hSysMenu);
3376                 if( menu->items[0].hSubMenu == MENU_DefSysPopup )
3377                     menu->items[0].hSubMenu = MENU_CopySysPopup();
3378             }
3379         }
3380
3381         if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3382             wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU32)(-1) );
3383
3384         if( wndPtr->hSysMenu )
3385             return GetSubMenu16(wndPtr->hSysMenu, 0);
3386     }
3387     return 0;
3388 }
3389
3390
3391 /*******************************************************************
3392  *         SetSystemMenu16    (USER.280)
3393  */
3394 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
3395 {
3396     return SetSystemMenu32( hwnd, hMenu );
3397 }
3398
3399
3400 /*******************************************************************
3401  *         SetSystemMenu32    (USER32.508)
3402  */
3403 BOOL32 WINAPI SetSystemMenu32( HWND32 hwnd, HMENU32 hMenu )
3404 {
3405     WND *wndPtr = WIN_FindWndPtr(hwnd);
3406
3407     if (wndPtr)
3408     {
3409         if (wndPtr->hSysMenu) DestroyMenu32( wndPtr->hSysMenu );
3410         wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3411         return TRUE;
3412     }
3413     return FALSE;
3414 }
3415
3416
3417 /**********************************************************************
3418  *         GetMenu16    (USER.157)
3419  */
3420 HMENU16 WINAPI GetMenu16( HWND16 hWnd ) 
3421 {
3422     WND * wndPtr = WIN_FindWndPtr(hWnd);
3423     if (wndPtr && !(wndPtr->dwStyle & WS_CHILD)) 
3424         return (HMENU16)wndPtr->wIDmenu;
3425     return 0;
3426 }
3427
3428
3429 /**********************************************************************
3430  *         GetMenu32    (USER32.257)
3431  */
3432 HMENU32 WINAPI GetMenu32( HWND32 hWnd ) 
3433
3434     WND * wndPtr = WIN_FindWndPtr(hWnd);
3435     if (wndPtr && !(wndPtr->dwStyle & WS_CHILD)) 
3436         return (HMENU32)wndPtr->wIDmenu;
3437     return 0;
3438 }
3439
3440
3441 /**********************************************************************
3442  *         SetMenu16    (USER.158)
3443  */
3444 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
3445 {
3446     return SetMenu32( hWnd, hMenu );
3447 }
3448
3449
3450 /**********************************************************************
3451  *         SetMenu32    (USER32.487)
3452  */
3453 BOOL32 WINAPI SetMenu32( HWND32 hWnd, HMENU32 hMenu )
3454 {
3455     WND * wndPtr = WIN_FindWndPtr(hWnd);
3456
3457     TRACE(menu,"(%04x, %04x);\n", hWnd, hMenu);
3458
3459     if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
3460     {
3461         if (GetCapture32() == hWnd) ReleaseCapture();
3462
3463         wndPtr->wIDmenu = (UINT32)hMenu;
3464         if (hMenu != 0)
3465         {
3466             LPPOPUPMENU lpmenu;
3467
3468             if (!(lpmenu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return FALSE;
3469             lpmenu->hWnd = hWnd;
3470             lpmenu->wFlags &= ~MF_POPUP;  /* Can't be a popup */
3471             lpmenu->Height = 0;  /* Make sure we recalculate the size */
3472         }
3473         if (IsWindowVisible32(hWnd))
3474             SetWindowPos32( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3475                         SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3476         return TRUE;
3477     }
3478     return FALSE;
3479 }
3480
3481
3482
3483 /**********************************************************************
3484  *         GetSubMenu16    (USER.159)
3485  */
3486 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
3487 {
3488     return GetSubMenu32( hMenu, nPos );
3489 }
3490
3491
3492 /**********************************************************************
3493  *         GetSubMenu32    (USER32.288)
3494  */
3495 HMENU32 WINAPI GetSubMenu32( HMENU32 hMenu, INT32 nPos )
3496 {
3497     LPPOPUPMENU lppop;
3498
3499     if (!(lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu))) return 0;
3500     if ((UINT32)nPos >= lppop->nItems) return 0;
3501     if (!(lppop->items[nPos].fType & MF_POPUP)) return 0;
3502     return lppop->items[nPos].hSubMenu;
3503 }
3504
3505
3506 /**********************************************************************
3507  *         DrawMenuBar16    (USER.160)
3508  */
3509 void WINAPI DrawMenuBar16( HWND16 hWnd )
3510 {
3511     DrawMenuBar32( hWnd );
3512 }
3513
3514
3515 /**********************************************************************
3516  *         DrawMenuBar32    (USER32.161)
3517  */
3518 BOOL32 WINAPI DrawMenuBar32( HWND32 hWnd )
3519 {
3520     LPPOPUPMENU lppop;
3521     WND *wndPtr = WIN_FindWndPtr(hWnd);
3522     if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
3523     {
3524         lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR((HMENU16)wndPtr->wIDmenu);
3525         if (lppop == NULL) return FALSE;
3526
3527         lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3528         SetWindowPos32( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3529                         SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3530         return TRUE;
3531     }
3532     return FALSE;
3533 }
3534
3535
3536 /***********************************************************************
3537  *           EndMenu   (USER.187) (USER32.175)
3538  */
3539 void WINAPI EndMenu(void)
3540 {
3541     fEndMenu = TRUE;
3542 }
3543
3544
3545 /***********************************************************************
3546  *           LookupMenuHandle   (USER.217)
3547  */
3548 HMENU16 WINAPI LookupMenuHandle( HMENU16 hmenu, INT16 id )
3549 {
3550     HMENU32 hmenu32 = hmenu;
3551     INT32 id32 = id;
3552     if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3553     else return hmenu32;
3554 }
3555
3556
3557 /**********************************************************************
3558  *          LoadMenu16    (USER.150)
3559  */
3560 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, SEGPTR name )
3561 {
3562     HRSRC16 hRsrc;
3563     HGLOBAL16 handle;
3564     HMENU16 hMenu;
3565
3566     if (HIWORD(name))
3567     {
3568         char *str = (char *)PTR_SEG_TO_LIN( name );
3569         TRACE(menu, "(%04x,'%s')\n", instance, str );
3570         if (str[0] == '#') name = (SEGPTR)atoi( str + 1 );
3571     }
3572     else
3573         TRACE(resource,"(%04x,%04x)\n",instance,LOWORD(name));
3574
3575     if (!name) return 0;
3576     
3577     /* check for Win32 module */
3578     if (HIWORD(instance))
3579         return LoadMenu32A(instance,PTR_SEG_TO_LIN(name));
3580     instance = GetExePtr( instance );
3581
3582     if (!(hRsrc = FindResource16( instance, name, RT_MENU16 ))) return 0;
3583     if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3584     hMenu = LoadMenuIndirect16(LockResource16(handle));
3585     FreeResource16( handle );
3586     return hMenu;
3587 }
3588
3589
3590 /*****************************************************************
3591  *        LoadMenu32A   (USER32.370)
3592  */
3593 HMENU32 WINAPI LoadMenu32A( HINSTANCE32 instance, LPCSTR name )
3594 {
3595     HRSRC32 hrsrc = FindResource32A( instance, name, RT_MENU32A );
3596     if (!hrsrc) return 0;
3597     return LoadMenuIndirect32A( (LPCVOID)LoadResource32( instance, hrsrc ));
3598 }
3599
3600
3601 /*****************************************************************
3602  *        LoadMenu32W   (USER32.373)
3603  */
3604 HMENU32 WINAPI LoadMenu32W( HINSTANCE32 instance, LPCWSTR name )
3605 {
3606     HRSRC32 hrsrc = FindResource32W( instance, name, RT_MENU32W );
3607     if (!hrsrc) return 0;
3608     return LoadMenuIndirect32W( (LPCVOID)LoadResource32( instance, hrsrc ));
3609 }
3610
3611
3612 /**********************************************************************
3613  *          LoadMenuIndirect16    (USER.220)
3614  */
3615 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3616 {
3617     HMENU16 hMenu;
3618     WORD version, offset;
3619     LPCSTR p = (LPCSTR)template;
3620
3621     TRACE(menu,"(%p)\n", template );
3622     version = GET_WORD(p);
3623     p += sizeof(WORD);
3624     if (version)
3625     {
3626         WARN(menu, "version must be 0 for Win16\n" );
3627         return 0;
3628     }
3629     offset = GET_WORD(p);
3630     p += sizeof(WORD) + offset;
3631     if (!(hMenu = CreateMenu32())) return 0;
3632     if (!MENU_ParseResource( p, hMenu, FALSE ))
3633     {
3634         DestroyMenu32( hMenu );
3635         return 0;
3636     }
3637     return hMenu;
3638 }
3639
3640
3641 /**********************************************************************
3642  *          LoadMenuIndirect32A    (USER32.371)
3643  */
3644 HMENU32 WINAPI LoadMenuIndirect32A( LPCVOID template )
3645 {
3646     HMENU16 hMenu;
3647     WORD version, offset;
3648     LPCSTR p = (LPCSTR)template;
3649
3650     TRACE(menu,"%p\n", template );
3651     version = GET_WORD(p);
3652     p += sizeof(WORD);
3653     switch (version)
3654       {
3655       case 0:
3656         offset = GET_WORD(p);
3657         p += sizeof(WORD) + offset;
3658         if (!(hMenu = CreateMenu32())) return 0;
3659         if (!MENU_ParseResource( p, hMenu, TRUE ))
3660           {
3661             DestroyMenu32( hMenu );
3662             return 0;
3663           }
3664         return hMenu;
3665       case 1:
3666         offset = GET_WORD(p);
3667         p += sizeof(WORD) + offset;
3668         if (!(hMenu = CreateMenu32())) return 0;
3669         if (!MENUEX_ParseResource( p, hMenu))
3670           {
3671             DestroyMenu32( hMenu );
3672             return 0;
3673           }
3674         return hMenu;
3675       default:
3676         ERR(menu, "version %d not supported.\n", version);
3677         return 0;
3678       }
3679 }
3680
3681
3682 /**********************************************************************
3683  *          LoadMenuIndirect32W    (USER32.372)
3684  */
3685 HMENU32 WINAPI LoadMenuIndirect32W( LPCVOID template )
3686 {
3687     /* FIXME: is there anything different between A and W? */
3688     return LoadMenuIndirect32A( template );
3689 }
3690
3691
3692 /**********************************************************************
3693  *              IsMenu16    (USER.358)
3694  */
3695 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
3696 {
3697     LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
3698     return IS_A_MENU(menu);
3699 }
3700
3701
3702 /**********************************************************************
3703  *              IsMenu32    (USER32.346)
3704  */
3705 BOOL32 WINAPI IsMenu32(HMENU32 hmenu)
3706 {
3707     LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
3708     return IS_A_MENU(menu);
3709 }
3710
3711 /**********************************************************************
3712  *              GetMenuItemInfo32_common
3713  */
3714
3715 static BOOL32 GetMenuItemInfo32_common ( HMENU32 hmenu, UINT32 item,
3716                                          BOOL32 bypos,
3717                                          LPMENUITEMINFO32A lpmii,
3718                                          BOOL32 unicode)
3719 {
3720   MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
3721     debug_print_menuitem("GetMenuItemInfo32_common: ", menu, "");
3722     if (!menu)
3723         return FALSE;
3724
3725     if (lpmii->fMask & MIIM_TYPE) {
3726         lpmii->fType = menu->fType;
3727         switch (MENU_ITEM_TYPE(menu->fType)) {
3728         case MF_STRING:
3729             if (menu->text && lpmii->dwTypeData && lpmii->cch) {
3730           if (unicode)
3731                     lstrcpynAtoW((LPWSTR) lpmii->dwTypeData,
3732                                  menu->text,
3733                                  lpmii->cch);
3734                 else
3735                     lstrcpyn32A(lpmii->dwTypeData,
3736                                 menu->text,
3737                                 lpmii->cch);
3738         }
3739             break;
3740         case MF_OWNERDRAW:
3741         case MF_BITMAP:
3742             lpmii->dwTypeData = menu->text;
3743             break;
3744         default:
3745             break;
3746     }
3747     }
3748     if (lpmii->fMask & MIIM_STATE)
3749         lpmii->fState = menu->fState;
3750
3751     if (lpmii->fMask & MIIM_ID)
3752         lpmii->wID = menu->wID;
3753
3754     if (lpmii->fMask & MIIM_SUBMENU)
3755         lpmii->hSubMenu = menu->hSubMenu;
3756
3757     if (lpmii->fMask & MIIM_CHECKMARKS) {
3758         lpmii->hbmpChecked = menu->hCheckBit;
3759         lpmii->hbmpUnchecked = menu->hUnCheckBit;
3760     }
3761     if (lpmii->fMask & MIIM_DATA)
3762         lpmii->dwItemData = menu->dwItemData;
3763
3764   return TRUE;
3765 }
3766
3767 /**********************************************************************
3768  *              GetMenuItemInfo32A    (USER32.264)
3769  */
3770 BOOL32 WINAPI GetMenuItemInfo32A( HMENU32 hmenu, UINT32 item, BOOL32 bypos,
3771                                   LPMENUITEMINFO32A lpmii)
3772 {
3773     return GetMenuItemInfo32_common (hmenu, item, bypos, lpmii, FALSE);
3774 }
3775
3776 /**********************************************************************
3777  *              GetMenuItemInfo32W    (USER32.265)
3778  */
3779 BOOL32 WINAPI GetMenuItemInfo32W( HMENU32 hmenu, UINT32 item, BOOL32 bypos,
3780                                   LPMENUITEMINFO32W lpmii)
3781 {
3782     return GetMenuItemInfo32_common (hmenu, item, bypos,
3783                                      (LPMENUITEMINFO32A)lpmii, TRUE);
3784 }
3785
3786 /**********************************************************************
3787  *              SetMenuItemInfo32_common
3788  */
3789
3790 static BOOL32 SetMenuItemInfo32_common(MENUITEM * menu,
3791                                        const MENUITEMINFO32A *lpmii,
3792                                        BOOL32 unicode)
3793 {
3794     if (!menu) return FALSE;
3795
3796     if (lpmii->fMask & MIIM_TYPE) {
3797         /* Get rid of old string.  */
3798         if (IS_STRING_ITEM(menu->fType) && menu->text)
3799             HeapFree(SystemHeap, 0, menu->text);
3800
3801         menu->fType = lpmii->fType;
3802         menu->text = lpmii->dwTypeData;
3803         if (IS_STRING_ITEM(menu->fType) && menu->text) {
3804             menu->text =
3805                 unicode
3806                 ? HEAP_strdupWtoA(SystemHeap, 0,
3807                                   (LPWSTR) lpmii->dwTypeData)
3808                 : HEAP_strdupA(SystemHeap, 0, lpmii->dwTypeData);
3809         }
3810     }
3811     if (lpmii->fMask & MIIM_STATE)
3812         menu->fState = lpmii->fState;
3813
3814     if (lpmii->fMask & MIIM_ID)
3815         menu->wID = lpmii->wID;
3816
3817     if (lpmii->fMask & MIIM_SUBMENU)
3818         menu->hSubMenu = lpmii->hSubMenu;
3819
3820     if (lpmii->fMask & MIIM_CHECKMARKS)
3821     {
3822         menu->hCheckBit = lpmii->hbmpChecked;
3823         menu->hUnCheckBit = lpmii->hbmpUnchecked;
3824     }
3825     if (lpmii->fMask & MIIM_DATA)
3826         menu->dwItemData = lpmii->dwItemData;
3827
3828     debug_print_menuitem("SetMenuItemInfo32_common: ", menu, "");
3829     return TRUE;
3830 }
3831
3832 /**********************************************************************
3833  *              SetMenuItemInfo32A    (USER32.491)
3834  */
3835 BOOL32 WINAPI SetMenuItemInfo32A(HMENU32 hmenu, UINT32 item, BOOL32 bypos,
3836                                  const MENUITEMINFO32A *lpmii) 
3837 {
3838     return SetMenuItemInfo32_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
3839                                     lpmii, FALSE);
3840 }
3841
3842 /**********************************************************************
3843  *              SetMenuItemInfo32W    (USER32.492)
3844  */
3845 BOOL32 WINAPI SetMenuItemInfo32W(HMENU32 hmenu, UINT32 item, BOOL32 bypos,
3846                                  const MENUITEMINFO32W *lpmii)
3847 {
3848     return SetMenuItemInfo32_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
3849                                     (const MENUITEMINFO32A*)lpmii, TRUE);
3850 }
3851
3852 /**********************************************************************
3853  *              SetMenuDefaultItem32    (USER32.489)
3854  */
3855 BOOL32 WINAPI SetMenuDefaultItem32(HMENU32 hmenu, UINT32 item, BOOL32 bypos)
3856 {
3857     MENUITEM *menuitem = MENU_FindItem(&hmenu, &item, bypos);
3858     POPUPMENU *menu;
3859
3860     if (!menuitem) return FALSE;
3861     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hmenu))) return FALSE;
3862
3863     menu->defitem = item; /* position */
3864
3865     debug_print_menuitem("SetMenuDefaultItem32: ", menuitem, "");
3866     FIXME(menu, "(0x%x,%d,%d), empty stub!\n",
3867                   hmenu, item, bypos);
3868     return TRUE;
3869 }
3870
3871 /**********************************************************************
3872  *              GetMenuDefaultItem32    (USER32.260)
3873  */
3874 UINT32 WINAPI GetMenuDefaultItem32(HMENU32 hmenu, UINT32 bypos, UINT32 flags)
3875 {
3876     POPUPMENU *menu;
3877
3878     if (!(menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hmenu))) return 0; /*FIXME*/
3879
3880     FIXME(menu, "(0x%x,%d,%d), stub!\n", hmenu, bypos, flags);
3881     if (bypos & MF_BYPOSITION)
3882         return menu->defitem;
3883     else
3884         return menu->items[menu->defitem].wID;
3885 }
3886
3887 /*******************************************************************
3888  *              InsertMenuItem16   (USER.441)
3889  *
3890  * FIXME: untested
3891  */
3892 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
3893                                 const MENUITEMINFO16 *mii )
3894 {
3895     MENUITEMINFO32A miia;
3896
3897     miia.cbSize        = sizeof(miia);
3898     miia.fMask         = mii->fMask;
3899     miia.dwTypeData    = mii->dwTypeData;
3900     miia.fType         = mii->fType;
3901     miia.fState        = mii->fState;
3902     miia.wID           = mii->wID;
3903     miia.hSubMenu      = mii->hSubMenu;
3904     miia.hbmpChecked   = mii->hbmpChecked;
3905     miia.hbmpUnchecked = mii->hbmpUnchecked;
3906     miia.dwItemData    = mii->dwItemData;
3907     miia.cch           = mii->cch;
3908     if (IS_STRING_ITEM(miia.fType))
3909         miia.dwTypeData = PTR_SEG_TO_LIN(miia.dwTypeData);
3910     return InsertMenuItem32A( hmenu, pos, byposition, &miia );
3911 }
3912
3913
3914 /**********************************************************************
3915  *              InsertMenuItem32A    (USER32.323)
3916  */
3917 BOOL32 WINAPI InsertMenuItem32A(HMENU32 hMenu, UINT32 uItem, BOOL32 bypos,
3918                                 const MENUITEMINFO32A *lpmii)
3919 {
3920     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
3921     return SetMenuItemInfo32_common(item, lpmii, FALSE);
3922 }
3923
3924
3925 /**********************************************************************
3926  *              InsertMenuItem32W    (USER32.324)
3927  */
3928 BOOL32 WINAPI InsertMenuItem32W(HMENU32 hMenu, UINT32 uItem, BOOL32 bypos,
3929                                 const MENUITEMINFO32W *lpmii)
3930 {
3931     MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
3932     return SetMenuItemInfo32_common(item, (const MENUITEMINFO32A*)lpmii, TRUE);
3933 }
3934
3935 /**********************************************************************
3936  *              CheckMenuRadioItem32    (USER32.47)
3937  */
3938
3939 BOOL32 WINAPI CheckMenuRadioItem32(HMENU32 hMenu,
3940                                    UINT32 first, UINT32 last, UINT32 check,
3941                                    BOOL32 bypos)
3942 {
3943      MENUITEM *mifirst, *milast, *micheck;
3944      HMENU32 mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
3945
3946      TRACE(menu, "ox%x: %d-%d, check %d, bypos=%d\n",
3947                   hMenu, first, last, check, bypos);
3948
3949      mifirst = MENU_FindItem (&mfirst, &first, bypos);
3950      milast = MENU_FindItem (&mlast, &last, bypos);
3951      micheck = MENU_FindItem (&mcheck, &check, bypos);
3952
3953      if (mifirst == NULL || milast == NULL || micheck == NULL ||
3954          mifirst > milast || mfirst != mlast || mfirst != mcheck ||
3955          micheck > milast || micheck < mifirst)
3956           return FALSE;
3957
3958      while (mifirst <= milast)
3959      {
3960           if (mifirst == micheck)
3961           {
3962                mifirst->fType |= MFT_RADIOCHECK;
3963                mifirst->fState |= MFS_CHECKED;
3964           } else {
3965                mifirst->fType &= ~MFT_RADIOCHECK;
3966                mifirst->fState &= ~MFS_CHECKED;
3967           }
3968           mifirst++;
3969      }
3970
3971      return TRUE;
3972 }
3973
3974 /**********************************************************************
3975  *              CheckMenuRadioItem16    (not a Windows API)
3976  */
3977
3978 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
3979                                    UINT16 first, UINT16 last, UINT16 check,
3980                                    BOOL16 bypos)
3981 {
3982      return CheckMenuRadioItem32 (hMenu, first, last, check, bypos);
3983 }
3984
3985 /**********************************************************************
3986  *              GetMenuItemRect32    (USER32.266)
3987  */
3988
3989 BOOL32 WINAPI GetMenuItemRect32 (HWND32 hwnd, HMENU32 hMenu, UINT32 uItem,
3990                                  LPRECT32 rect)
3991 {
3992      RECT32 saverect, clientrect;
3993      BOOL32 barp;
3994      HDC32 hdc;
3995      WND *wndPtr;
3996      MENUITEM *item;
3997      HMENU32 orghMenu = hMenu;
3998
3999      TRACE(menu, "(0x%x,0x%x,%d,%p)\n",
4000                   hwnd, hMenu, uItem, rect);
4001
4002      item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4003      wndPtr = WIN_FindWndPtr (hwnd);
4004      if (!rect || !item || !wndPtr) return FALSE;
4005
4006      GetClientRect32( hwnd, &clientrect );
4007      hdc = GetDCEx32( hwnd, 0, DCX_CACHE | DCX_WINDOW );
4008      barp = (hMenu == orghMenu);
4009
4010      saverect = item->rect;
4011      MENU_CalcItemSize (hdc, item, hwnd,
4012                         clientrect.left, clientrect.top, barp);
4013      *rect = item->rect;
4014      item->rect = saverect;
4015
4016      ReleaseDC32( hwnd, hdc );
4017      return TRUE;
4018 }
4019
4020 /**********************************************************************
4021  *              GetMenuItemRect16    (USER.665)
4022  */
4023
4024 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4025                                  LPRECT16 rect)
4026 {
4027      RECT32 r32;
4028      BOOL32 res;
4029
4030      if (!rect) return FALSE;
4031      res = GetMenuItemRect32 (hwnd, hMenu, uItem, &r32);
4032      CONV_RECT32TO16 (&r32, rect);
4033      return res;
4034 }