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