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