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