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