Release 20010216.
[wine/multimedia.git] / controls / menu.c
blob9e756354aeb211f4046331e0c34a6bbcd0292fe8
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 */
9 /*
10 * Note: the style MF_MOUSESELECT is used to mark popup items that
11 * have been selected, i.e. their popup menu is currently displayed.
12 * This is probably not the meaning this style has in MS-Windows.
15 #include <assert.h>
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include "windef.h"
21 #include "winnls.h"
22 #include "wingdi.h"
23 #include "wine/winbase16.h"
24 #include "wine/winuser16.h"
25 #include "wine/unicode.h"
26 #include "wine/port.h"
27 #include "win.h"
28 #include "task.h"
29 #include "heap.h"
30 #include "controls.h"
31 #include "nonclient.h"
32 #include "user.h"
33 #include "message.h"
34 #include "queue.h"
36 #include "debugtools.h"
38 DEFAULT_DEBUG_CHANNEL(menu);
39 DECLARE_DEBUG_CHANNEL(accel);
41 /* internal popup menu window messages */
43 #define MM_SETMENUHANDLE (WM_USER + 0)
44 #define MM_GETMENUHANDLE (WM_USER + 1)
46 /* Menu item structure */
47 typedef struct {
48 /* ----------- MENUITEMINFO Stuff ----------- */
49 UINT fType; /* Item type. */
50 UINT fState; /* Item state. */
51 UINT wID; /* Item id. */
52 HMENU hSubMenu; /* Pop-up menu. */
53 HBITMAP hCheckBit; /* Bitmap when checked. */
54 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
55 LPWSTR text; /* Item text or bitmap handle. */
56 DWORD dwItemData; /* Application defined. */
57 DWORD dwTypeData; /* depends on fMask */
58 HBITMAP hbmpItem; /* bitmap in win98 style menus */
59 /* ----------- Wine stuff ----------- */
60 RECT rect; /* Item area (relative to menu window) */
61 UINT xTab; /* X position of text after Tab */
62 } MENUITEM;
64 /* Popup menu structure */
65 typedef struct {
66 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
67 WORD wMagic; /* Magic number */
68 HQUEUE16 hTaskQ; /* Task queue for this menu */
69 WORD Width; /* Width of the whole menu */
70 WORD Height; /* Height of the whole menu */
71 WORD nItems; /* Number of items in the menu */
72 HWND hWnd; /* Window containing the menu */
73 MENUITEM *items; /* Array of menu items */
74 UINT FocusedItem; /* Currently focused item */
75 HWND hwndOwner; /* window receiving the messages for ownerdraw */
76 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
77 /* ------------ MENUINFO members ------ */
78 DWORD dwStyle; /* Extended mennu style */
79 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
80 HBRUSH hbrBack; /* brush for menu background */
81 DWORD dwContextHelpID;
82 DWORD dwMenuData; /* application defined value */
83 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
84 } POPUPMENU, *LPPOPUPMENU;
86 /* internal flags for menu tracking */
88 #define TF_ENDMENU 0x0001
89 #define TF_SUSPENDPOPUP 0x0002
90 #define TF_SKIPREMOVE 0x0004
92 typedef struct
94 UINT trackFlags;
95 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
96 HMENU hTopMenu; /* initial menu */
97 HWND hOwnerWnd; /* where notifications are sent */
98 POINT pt;
99 } MTRACKER;
101 #define MENU_MAGIC 0x554d /* 'MU' */
102 #define IS_A_MENU(pmenu) ((pmenu) && (pmenu)->wMagic == MENU_MAGIC)
104 #define ITEM_PREV -1
105 #define ITEM_NEXT 1
107 /* Internal MENU_TrackMenu() flags */
108 #define TPM_INTERNAL 0xF0000000
109 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
110 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
111 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
113 /* popup menu shade thickness */
114 #define POPUP_XSHADE 4
115 #define POPUP_YSHADE 4
117 /* Space between 2 menu bar items */
118 #define MENU_BAR_ITEMS_SPACE 12
120 /* Minimum width of a tab character */
121 #define MENU_TAB_SPACE 8
123 /* Height of a separator item */
124 #define SEPARATOR_HEIGHT 5
126 /* (other menu->FocusedItem values give the position of the focused item) */
127 #define NO_SELECTED_ITEM 0xffff
129 #define MENU_ITEM_TYPE(flags) \
130 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
132 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
133 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
134 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
136 #define IS_SYSTEM_MENU(menu) \
137 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
139 #define IS_SYSTEM_POPUP(menu) \
140 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
142 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
143 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
144 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
145 MF_POPUP | MF_SYSMENU | MF_HELP)
146 #define STATE_MASK (~TYPE_MASK)
148 /* Dimension of the menu bitmaps */
149 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
151 static HBITMAP hStdMnArrow = 0;
153 /* Minimze/restore/close buttons to be inserted in menubar */
154 static HBITMAP hBmpMinimize = 0;
155 static HBITMAP hBmpMinimizeD = 0;
156 static HBITMAP hBmpMaximize = 0;
157 static HBITMAP hBmpMaximizeD = 0;
158 static HBITMAP hBmpClose = 0;
159 static HBITMAP hBmpCloseD = 0;
162 static HBRUSH hShadeBrush = 0;
163 static HFONT hMenuFont = 0;
164 static HFONT hMenuFontBold = 0;
166 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
168 /* Use global popup window because there's no way 2 menus can
169 * be tracked at the same time. */
171 static WND* pTopPopupWnd = 0;
172 static UINT uSubPWndLevel = 0;
174 /* Flag set by EndMenu() to force an exit from menu tracking */
175 static BOOL fEndMenu = FALSE;
177 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
180 /*********************************************************************
181 * menu class descriptor
183 const struct builtin_class_descr MENU_builtin_class =
185 POPUPMENU_CLASS_ATOM, /* name */
186 CS_GLOBALCLASS | CS_SAVEBITS, /* style */
187 NULL, /* procA (winproc is Unicode only) */
188 PopupMenuWndProc, /* procW */
189 sizeof(HMENU), /* extra */
190 IDC_ARROWA, /* cursor */
191 COLOR_MENU+1 /* brush */
195 /***********************************************************************
196 * debug_print_menuitem
198 * Print a menuitem in readable form.
201 #define debug_print_menuitem(pre, mp, post) \
202 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
204 #define MENUOUT(text) \
205 DPRINTF("%s%s", (count++ ? "," : ""), (text))
207 #define MENUFLAG(bit,text) \
208 do { \
209 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
210 } while (0)
212 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
213 const char *postfix)
215 TRACE("%s ", prefix);
216 if (mp) {
217 UINT flags = mp->fType;
218 int typ = MENU_ITEM_TYPE(flags);
219 DPRINTF( "{ ID=0x%x", mp->wID);
220 if (flags & MF_POPUP)
221 DPRINTF( ", Sub=0x%x", mp->hSubMenu);
222 if (flags) {
223 int count = 0;
224 DPRINTF( ", Typ=");
225 if (typ == MFT_STRING)
226 /* Nothing */ ;
227 else if (typ == MFT_SEPARATOR)
228 MENUOUT("sep");
229 else if (typ == MFT_OWNERDRAW)
230 MENUOUT("own");
231 else if (typ == MFT_BITMAP)
232 MENUOUT("bit");
233 else
234 MENUOUT("???");
235 flags -= typ;
237 MENUFLAG(MF_POPUP, "pop");
238 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
239 MENUFLAG(MFT_MENUBREAK, "brk");
240 MENUFLAG(MFT_RADIOCHECK, "radio");
241 MENUFLAG(MFT_RIGHTORDER, "rorder");
242 MENUFLAG(MF_SYSMENU, "sys");
243 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
245 if (flags)
246 DPRINTF( "+0x%x", flags);
248 flags = mp->fState;
249 if (flags) {
250 int count = 0;
251 DPRINTF( ", State=");
252 MENUFLAG(MFS_GRAYED, "grey");
253 MENUFLAG(MFS_DEFAULT, "default");
254 MENUFLAG(MFS_DISABLED, "dis");
255 MENUFLAG(MFS_CHECKED, "check");
256 MENUFLAG(MFS_HILITE, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
258 MENUFLAG(MF_MOUSESELECT, "mouse");
259 if (flags)
260 DPRINTF( "+0x%x", flags);
262 if (mp->hCheckBit)
263 DPRINTF( ", Chk=0x%x", mp->hCheckBit);
264 if (mp->hUnCheckBit)
265 DPRINTF( ", Unc=0x%x", mp->hUnCheckBit);
267 if (typ == MFT_STRING) {
268 if (mp->text)
269 DPRINTF( ", Text=%s", debugstr_w(mp->text));
270 else
271 DPRINTF( ", Text=Null");
272 } else if (mp->text == NULL)
273 /* Nothing */ ;
274 else
275 DPRINTF( ", Text=%p", mp->text);
276 if (mp->dwItemData)
277 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
278 DPRINTF( " }");
279 } else {
280 DPRINTF( "NULL");
283 DPRINTF(" %s\n", postfix);
286 #undef MENUOUT
287 #undef MENUFLAG
290 /***********************************************************************
291 * MENU_GetMenu
293 * Validate the given menu handle and returns the menu structure pointer.
295 POPUPMENU *MENU_GetMenu(HMENU hMenu)
297 POPUPMENU *menu;
298 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
299 if (!IS_A_MENU(menu))
301 WARN("invalid menu handle=%x, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 menu = NULL;
304 return menu;
307 /***********************************************************************
308 * MENU_CopySysPopup
310 * Return the default system menu.
312 static HMENU MENU_CopySysPopup(void)
314 HMENU hMenu = LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
316 if( hMenu ) {
317 POPUPMENU* menu = (POPUPMENU *) USER_HEAP_LIN_ADDR(hMenu);
318 menu->wFlags |= MF_SYSMENU | MF_POPUP;
319 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
321 else {
322 hMenu = 0;
323 ERR("Unable to load default system menu\n" );
326 TRACE("returning %x.\n", hMenu );
328 return hMenu;
331 /***********************************************************************
332 * MENU_GetTopPopupWnd()
334 * Return the locked pointer pTopPopupWnd.
336 static WND *MENU_GetTopPopupWnd()
338 return WIN_LockWndPtr(pTopPopupWnd);
340 /***********************************************************************
341 * MENU_ReleaseTopPopupWnd()
343 * Release the locked pointer pTopPopupWnd.
345 static void MENU_ReleaseTopPopupWnd()
347 WIN_ReleaseWndPtr(pTopPopupWnd);
349 /***********************************************************************
350 * MENU_DestroyTopPopupWnd()
352 * Destroy the locked pointer pTopPopupWnd.
354 static void MENU_DestroyTopPopupWnd()
356 WND *tmpWnd = pTopPopupWnd;
357 pTopPopupWnd = NULL;
358 WIN_ReleaseWndPtr(tmpWnd);
363 /**********************************************************************
364 * MENU_GetSysMenu
366 * Create a copy of the system menu. System menu in Windows is
367 * a special menu bar with the single entry - system menu popup.
368 * This popup is presented to the outside world as a "system menu".
369 * However, the real system menu handle is sometimes seen in the
370 * WM_MENUSELECT parameters (and Word 6 likes it this way).
372 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
374 HMENU hMenu;
376 if ((hMenu = CreateMenu()))
378 POPUPMENU *menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hMenu);
379 menu->wFlags = MF_SYSMENU;
380 menu->hWnd = hWnd;
382 if (hPopupMenu == (HMENU)(-1))
383 hPopupMenu = MENU_CopySysPopup();
384 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
386 if (hPopupMenu)
388 InsertMenuA( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION, hPopupMenu, NULL );
390 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
391 menu->items[0].fState = 0;
392 menu = (POPUPMENU*) USER_HEAP_LIN_ADDR(hPopupMenu);
393 menu->wFlags |= MF_SYSMENU;
395 TRACE("GetSysMenu hMenu=%04x (%04x)\n", hMenu, hPopupMenu );
396 return hMenu;
398 DestroyMenu( hMenu );
400 ERR("failed to load system menu!\n");
401 return 0;
405 /***********************************************************************
406 * MENU_Init
408 * Menus initialisation.
410 BOOL MENU_Init()
412 HBITMAP hBitmap;
413 NONCLIENTMETRICSA ncm;
415 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
416 0x55, 0, 0xAA, 0,
417 0x55, 0, 0xAA, 0,
418 0x55, 0, 0xAA, 0 };
420 /* Load menu bitmaps */
421 hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
422 /* Load system buttons bitmaps */
423 hBmpMinimize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCE));
424 hBmpMinimizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_REDUCED));
425 hBmpMaximize = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORE));
426 hBmpMaximizeD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_RESTORED));
427 hBmpClose = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSE));
428 hBmpCloseD = LoadBitmapA(0,MAKEINTRESOURCEA(OBM_CLOSED));
430 if (hStdMnArrow)
432 BITMAP bm;
433 GetObjectA( hStdMnArrow, sizeof(bm), &bm );
434 arrow_bitmap_width = bm.bmWidth;
435 arrow_bitmap_height = bm.bmHeight;
436 } else
437 return FALSE;
439 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
440 return FALSE;
442 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
443 return FALSE;
445 DeleteObject( hBitmap );
446 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
447 return FALSE;
449 ncm.cbSize = sizeof (NONCLIENTMETRICSA);
450 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &ncm, 0)))
451 return FALSE;
453 if (!(hMenuFont = CreateFontIndirectA( &ncm.lfMenuFont )))
454 return FALSE;
456 ncm.lfMenuFont.lfWeight += 300;
457 if ( ncm.lfMenuFont.lfWeight > 1000)
458 ncm.lfMenuFont.lfWeight = 1000;
460 if (!(hMenuFontBold = CreateFontIndirectA( &ncm.lfMenuFont )))
461 return FALSE;
463 return TRUE;
466 /***********************************************************************
467 * MENU_InitSysMenuPopup
469 * Grey the appropriate items in System menu.
471 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
473 BOOL gray;
475 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
476 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
477 gray = ((style & WS_MAXIMIZE) != 0);
478 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
479 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
480 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
481 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
482 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
483 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
484 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
485 gray = (clsStyle & CS_NOCLOSE) != 0;
487 /* The menu item must keep its state if it's disabled */
488 if(gray)
489 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
493 /******************************************************************************
495 * UINT MENU_GetStartOfNextColumn(
496 * HMENU hMenu )
498 *****************************************************************************/
500 static UINT MENU_GetStartOfNextColumn(
501 HMENU hMenu )
503 POPUPMENU *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
504 UINT i = menu->FocusedItem + 1;
506 if(!menu)
507 return NO_SELECTED_ITEM;
509 if( i == NO_SELECTED_ITEM )
510 return i;
512 for( ; i < menu->nItems; ++i ) {
513 if (menu->items[i].fType & MF_MENUBARBREAK)
514 return i;
517 return NO_SELECTED_ITEM;
521 /******************************************************************************
523 * UINT MENU_GetStartOfPrevColumn(
524 * HMENU hMenu )
526 *****************************************************************************/
528 static UINT MENU_GetStartOfPrevColumn(
529 HMENU hMenu )
531 POPUPMENU const *menu = (POPUPMENU *)USER_HEAP_LIN_ADDR(hMenu);
532 UINT i;
534 if( !menu )
535 return NO_SELECTED_ITEM;
537 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
538 return NO_SELECTED_ITEM;
540 /* Find the start of the column */
542 for(i = menu->FocusedItem; i != 0 &&
543 !(menu->items[i].fType & MF_MENUBARBREAK);
544 --i); /* empty */
546 if(i == 0)
547 return NO_SELECTED_ITEM;
549 for(--i; i != 0; --i) {
550 if (menu->items[i].fType & MF_MENUBARBREAK)
551 break;
554 TRACE("ret %d.\n", i );
556 return i;
561 /***********************************************************************
562 * MENU_FindItem
564 * Find a menu item. Return a pointer on the item, and modifies *hmenu
565 * in case the item was in a sub-menu.
567 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
569 POPUPMENU *menu;
570 UINT i;
572 if (((*hmenu)==0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
573 if (wFlags & MF_BYPOSITION)
575 if (*nPos >= menu->nItems) return NULL;
576 return &menu->items[*nPos];
578 else
580 MENUITEM *item = menu->items;
581 for (i = 0; i < menu->nItems; i++, item++)
583 if (item->wID == *nPos)
585 *nPos = i;
586 return item;
588 else if (item->fType & MF_POPUP)
590 HMENU hsubmenu = item->hSubMenu;
591 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
592 if (subitem)
594 *hmenu = hsubmenu;
595 return subitem;
600 return NULL;
603 /***********************************************************************
604 * MENU_FindSubMenu
606 * Find a Sub menu. Return the position of the submenu, and modifies
607 * *hmenu in case it is found in another sub-menu.
608 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
610 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
612 POPUPMENU *menu;
613 UINT i;
614 MENUITEM *item;
615 if (((*hmenu)==0xffff) ||
616 (!(menu = MENU_GetMenu(*hmenu))))
617 return NO_SELECTED_ITEM;
618 item = menu->items;
619 for (i = 0; i < menu->nItems; i++, item++) {
620 if(!(item->fType & MF_POPUP)) continue;
621 if (item->hSubMenu == hSubTarget) {
622 return i;
624 else {
625 HMENU hsubmenu = item->hSubMenu;
626 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
627 if (pos != NO_SELECTED_ITEM) {
628 *hmenu = hsubmenu;
629 return pos;
633 return NO_SELECTED_ITEM;
636 /***********************************************************************
637 * MENU_FreeItemData
639 static void MENU_FreeItemData( MENUITEM* item )
641 /* delete text */
642 if (IS_STRING_ITEM(item->fType) && item->text)
643 HeapFree( SystemHeap, 0, item->text );
646 /***********************************************************************
647 * MENU_FindItemByCoords
649 * Find the item at the specified coordinates (screen coords). Does
650 * not work for child windows and therefore should not be called for
651 * an arbitrary system menu.
653 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
654 POINT pt, UINT *pos )
656 MENUITEM *item;
657 UINT i;
658 RECT wrect;
660 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
661 pt.x -= wrect.left;pt.y -= wrect.top;
662 item = menu->items;
663 for (i = 0; i < menu->nItems; i++, item++)
665 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
666 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
668 if (pos) *pos = i;
669 return item;
672 return NULL;
676 /***********************************************************************
677 * MENU_FindItemByKey
679 * Find the menu item selected by a key press.
680 * Return item id, -1 if none, -2 if we should close the menu.
682 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
683 UINT key, BOOL forceMenuChar )
685 TRACE("\tlooking for '%c' in [%04x]\n", (char)key, (UINT16)hmenu );
687 if (!IsMenu( hmenu ))
689 WND* w = WIN_FindWndPtr(hwndOwner);
690 hmenu = GetSubMenu(w->hSysMenu, 0);
691 WIN_ReleaseWndPtr(w);
694 if (hmenu)
696 POPUPMENU *menu = MENU_GetMenu( hmenu );
697 MENUITEM *item = menu->items;
698 LONG menuchar;
700 if( !forceMenuChar )
702 UINT i;
704 key = toupper(key);
705 for (i = 0; i < menu->nItems; i++, item++)
707 if (item->text && (IS_STRING_ITEM(item->fType)))
709 WCHAR *p = item->text - 2;
712 p = strchrW (p + 2, '&');
714 while (p != NULL && p [1] == '&');
715 if (p && (toupper(p[1]) == key)) return i;
719 menuchar = SendMessageA( hwndOwner, WM_MENUCHAR,
720 MAKEWPARAM( key, menu->wFlags ), hmenu );
721 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
722 if (HIWORD(menuchar) == 1) return (UINT)(-2);
724 return (UINT)(-1);
726 /***********************************************************************
727 * MENU_LoadMagicItem
729 * Load the bitmap associated with the magic menu item and its style
732 static HBITMAP MENU_LoadMagicItem(UINT id, BOOL hilite, DWORD dwItemData)
735 * Magic menu item id's section
736 * These magic id's are used by windows to insert "standard" mdi
737 * buttons (minimize,restore,close) on menu. Under windows,
738 * these magic id's make sure the right things appear when those
739 * bitmap buttons are pressed/selected/released.
742 switch(id & 0xffff)
743 { case HBMMENU_SYSTEM:
744 return (dwItemData) ?
745 (HBITMAP)dwItemData :
746 (hilite ? hBmpMinimizeD : hBmpMinimize);
747 case HBMMENU_MBAR_RESTORE:
748 return (hilite ? hBmpMaximizeD: hBmpMaximize);
749 case HBMMENU_MBAR_MINIMIZE:
750 return (hilite ? hBmpMinimizeD : hBmpMinimize);
751 case HBMMENU_MBAR_CLOSE:
752 return (hilite ? hBmpCloseD : hBmpClose);
753 case HBMMENU_CALLBACK:
754 case HBMMENU_MBAR_CLOSE_D:
755 case HBMMENU_MBAR_MINIMIZE_D:
756 case HBMMENU_POPUP_CLOSE:
757 case HBMMENU_POPUP_RESTORE:
758 case HBMMENU_POPUP_MAXIMIZE:
759 case HBMMENU_POPUP_MINIMIZE:
760 default:
761 FIXME("Magic 0x%08x not implemented\n", id);
762 return 0;
767 /***********************************************************************
768 * MENU_CalcItemSize
770 * Calculate the size of the menu item and store it in lpitem->rect.
772 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
773 INT orgX, INT orgY, BOOL menuBar )
775 WCHAR *p;
776 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
778 TRACE("dc=0x%04x owner=0x%04x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
779 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
780 (menuBar ? " (MenuBar)" : ""));
782 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
784 if (lpitem->fType & MF_OWNERDRAW)
787 ** Experimentation under Windows reveals that an owner-drawn
788 ** menu is expected to return the size of the content part of
789 ** the menu item, not including the checkmark nor the submenu
790 ** arrow. Windows adds those values itself and returns the
791 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
793 MEASUREITEMSTRUCT mis;
794 mis.CtlType = ODT_MENU;
795 mis.CtlID = 0;
796 mis.itemID = lpitem->wID;
797 mis.itemData = (DWORD)lpitem->dwItemData;
798 mis.itemHeight = 0;
799 mis.itemWidth = 0;
800 SendMessageA( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
801 lpitem->rect.right += mis.itemWidth;
803 if (menuBar)
805 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
808 /* under at least win95 you seem to be given a standard
809 height for the menu and the height value is ignored */
811 if (TWEAK_WineLook == WIN31_LOOK)
812 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
813 else
814 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
816 else
817 lpitem->rect.bottom += mis.itemHeight;
819 TRACE("id=%04x size=%dx%d\n",
820 lpitem->wID, mis.itemWidth, mis.itemHeight);
821 /* Fall through to get check/arrow width calculation. */
824 if (lpitem->fType & MF_SEPARATOR)
826 lpitem->rect.bottom += SEPARATOR_HEIGHT;
827 return;
830 if (!menuBar)
832 lpitem->rect.right += 2 * check_bitmap_width;
833 if (lpitem->fType & MF_POPUP)
834 lpitem->rect.right += arrow_bitmap_width;
837 if (lpitem->fType & MF_OWNERDRAW)
838 return;
840 if (IS_BITMAP_ITEM(lpitem->fType))
842 BITMAP bm;
843 HBITMAP resBmp = 0;
845 /* Check if there is a magic menu item associated with this item */
846 if (IS_MAGIC_ITEM(lpitem->text))
848 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fType & MF_HILITE),
849 lpitem->dwItemData);
851 else
852 resBmp = (HBITMAP)lpitem->text;
854 if (GetObjectA(resBmp, sizeof(bm), &bm ))
856 lpitem->rect.right += bm.bmWidth;
857 lpitem->rect.bottom += bm.bmHeight;
858 if (TWEAK_WineLook == WIN98_LOOK) {
859 /* Leave space for the sunken border */
860 lpitem->rect.right += 2;
861 lpitem->rect.bottom += 2;
868 /* If we get here, then it must be a text item */
869 if (IS_STRING_ITEM( lpitem->fType ))
870 { SIZE size;
872 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
874 lpitem->rect.right += size.cx;
875 if (TWEAK_WineLook == WIN31_LOOK)
876 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
877 else
878 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
879 lpitem->xTab = 0;
881 if (menuBar)
883 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
885 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
887 /* Item contains a tab (only meaningful in popup menus) */
888 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
889 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
890 lpitem->rect.right += MENU_TAB_SPACE;
892 else
894 if (strchrW( lpitem->text, '\b' ))
895 lpitem->rect.right += MENU_TAB_SPACE;
896 lpitem->xTab = lpitem->rect.right - check_bitmap_width
897 - arrow_bitmap_width;
900 TRACE("(%d,%d)-(%d,%d)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
904 /***********************************************************************
905 * MENU_PopupMenuCalcSize
907 * Calculate the size of a popup menu.
909 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
911 MENUITEM *lpitem;
912 HDC hdc;
913 int start, i;
914 int orgX, orgY, maxX, maxTab, maxTabWidth;
916 lppop->Width = lppop->Height = 0;
917 if (lppop->nItems == 0) return;
918 hdc = GetDC( 0 );
920 SelectObject( hdc, hMenuFont);
922 start = 0;
923 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
925 while (start < lppop->nItems)
927 lpitem = &lppop->items[start];
928 orgX = maxX;
929 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
931 maxTab = maxTabWidth = 0;
933 /* Parse items until column break or end of menu */
934 for (i = start; i < lppop->nItems; i++, lpitem++)
936 if ((i != start) &&
937 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
939 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
941 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
942 maxX = max( maxX, lpitem->rect.right );
943 orgY = lpitem->rect.bottom;
944 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
946 maxTab = max( maxTab, lpitem->xTab );
947 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
951 /* Finish the column (set all items to the largest width found) */
952 maxX = max( maxX, maxTab + maxTabWidth );
953 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
955 lpitem->rect.right = maxX;
956 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
957 lpitem->xTab = maxTab;
960 lppop->Height = max( lppop->Height, orgY );
963 lppop->Width = maxX;
965 /* space for 3d border */
966 if(TWEAK_WineLook > WIN31_LOOK)
968 lppop->Height += 2;
969 lppop->Width += 2;
972 ReleaseDC( 0, hdc );
976 /***********************************************************************
977 * MENU_MenuBarCalcSize
979 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
980 * height is off by 1 pixel which causes lengthy window relocations when
981 * active document window is maximized/restored.
983 * Calculate the size of the menu bar.
985 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
986 LPPOPUPMENU lppop, HWND hwndOwner )
988 MENUITEM *lpitem;
989 int start, i, orgX, orgY, maxY, helpPos;
991 if ((lprect == NULL) || (lppop == NULL)) return;
992 if (lppop->nItems == 0) return;
993 TRACE("left=%d top=%d right=%d bottom=%d\n",
994 lprect->left, lprect->top, lprect->right, lprect->bottom);
995 lppop->Width = lprect->right - lprect->left;
996 lppop->Height = 0;
997 maxY = lprect->top+1;
998 start = 0;
999 helpPos = -1;
1000 while (start < lppop->nItems)
1002 lpitem = &lppop->items[start];
1003 orgX = lprect->left;
1004 orgY = maxY;
1006 /* Parse items until line break or end of menu */
1007 for (i = start; i < lppop->nItems; i++, lpitem++)
1009 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1010 if ((i != start) &&
1011 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1013 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1014 orgX, orgY );
1015 debug_print_menuitem (" item: ", lpitem, "");
1016 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1018 if (lpitem->rect.right > lprect->right)
1020 if (i != start) break;
1021 else lpitem->rect.right = lprect->right;
1023 maxY = max( maxY, lpitem->rect.bottom );
1024 orgX = lpitem->rect.right;
1027 /* Finish the line (set all items to the largest height found) */
1028 while (start < i) lppop->items[start++].rect.bottom = maxY;
1031 lprect->bottom = maxY;
1032 lppop->Height = lprect->bottom - lprect->top;
1034 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1035 /* the last item (if several lines, only move the last line) */
1036 lpitem = &lppop->items[lppop->nItems-1];
1037 orgY = lpitem->rect.top;
1038 orgX = lprect->right;
1039 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1040 if ( (helpPos==-1) || (helpPos>i) )
1041 break; /* done */
1042 if (lpitem->rect.top != orgY) break; /* Other line */
1043 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1044 lpitem->rect.left += orgX - lpitem->rect.right;
1045 lpitem->rect.right = orgX;
1046 orgX = lpitem->rect.left;
1050 /***********************************************************************
1051 * MENU_DrawMenuItem
1053 * Draw a single menu item.
1055 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1056 UINT height, BOOL menuBar, UINT odaction )
1058 RECT rect;
1060 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1062 if (lpitem->fType & MF_SYSMENU)
1064 if( !IsIconic(hwnd) ) {
1065 if (TWEAK_WineLook > WIN31_LOOK)
1066 NC_DrawSysButton95( hwnd, hdc,
1067 lpitem->fState &
1068 (MF_HILITE | MF_MOUSESELECT) );
1069 else
1070 NC_DrawSysButton( hwnd, hdc,
1071 lpitem->fState &
1072 (MF_HILITE | MF_MOUSESELECT) );
1075 return;
1078 if (lpitem->fType & MF_OWNERDRAW)
1081 ** Experimentation under Windows reveals that an owner-drawn
1082 ** menu is given the rectangle which includes the space it requested
1083 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1084 ** and a popup-menu arrow. This is the value of lpitem->rect.
1085 ** Windows will leave all drawing to the application except for
1086 ** the popup-menu arrow. Windows always draws that itself, after
1087 ** the menu owner has finished drawing.
1089 DRAWITEMSTRUCT dis;
1091 dis.CtlType = ODT_MENU;
1092 dis.CtlID = 0;
1093 dis.itemID = lpitem->wID;
1094 dis.itemData = (DWORD)lpitem->dwItemData;
1095 dis.itemState = 0;
1096 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1097 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED;
1098 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1099 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1100 dis.hwndItem = hmenu;
1101 dis.hDC = hdc;
1102 dis.rcItem = lpitem->rect;
1103 TRACE("Ownerdraw: owner=%04x itemID=%d, itemState=%d, itemAction=%d, "
1104 "hwndItem=%04x, hdc=%04x, rcItem={%d,%d,%d,%d}\n", hwndOwner,
1105 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1106 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1107 dis.rcItem.bottom);
1108 SendMessageA( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1109 /* Fall through to draw popup-menu arrow */
1112 TRACE("rect={%d,%d,%d,%d}\n", lpitem->rect.left, lpitem->rect.top,
1113 lpitem->rect.right,lpitem->rect.bottom);
1115 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1117 rect = lpitem->rect;
1119 if (!(lpitem->fType & MF_OWNERDRAW))
1121 if (lpitem->fState & MF_HILITE)
1123 if(TWEAK_WineLook == WIN98_LOOK)
1125 if(menuBar)
1126 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1127 else
1128 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1130 else /* Not Win98 Look */
1132 if(!IS_BITMAP_ITEM(lpitem->fType))
1133 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1136 else
1137 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1140 SetBkMode( hdc, TRANSPARENT );
1142 if (!(lpitem->fType & MF_OWNERDRAW))
1144 /* vertical separator */
1145 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1147 if (TWEAK_WineLook > WIN31_LOOK)
1149 RECT rc = rect;
1150 rc.top = 3;
1151 rc.bottom = height - 3;
1152 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1154 else
1156 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1157 MoveToEx( hdc, rect.left, 0, NULL );
1158 LineTo( hdc, rect.left, height );
1162 /* horizontal separator */
1163 if (lpitem->fType & MF_SEPARATOR)
1165 if (TWEAK_WineLook > WIN31_LOOK)
1167 RECT rc = rect;
1168 rc.left++;
1169 rc.right--;
1170 rc.top += SEPARATOR_HEIGHT / 2;
1171 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1173 else
1175 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1176 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1177 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1179 return;
1183 /* Setup colors */
1185 if (lpitem->fState & MF_HILITE)
1187 if(TWEAK_WineLook == WIN98_LOOK)
1189 if(menuBar) {
1190 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1191 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1192 } else {
1193 if(lpitem->fState & MF_GRAYED)
1194 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1195 else
1196 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1197 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1200 else /* Not Win98 Look */
1202 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1203 if(!IS_BITMAP_ITEM(lpitem->fType))
1204 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1207 else
1209 if (lpitem->fState & MF_GRAYED)
1210 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1211 else
1212 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1213 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1216 /* helper lines for debugging */
1217 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1218 SelectObject( hdc, GetSysColorPen(COLOR_WINDOWFRAME) );
1219 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1220 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1223 if (!menuBar)
1225 INT y = rect.top + rect.bottom;
1226 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1227 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1229 if (!(lpitem->fType & MF_OWNERDRAW))
1231 /* Draw the check mark
1233 * FIXME:
1234 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1236 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1237 if (bm) /* we have a custom bitmap */
1239 HDC hdcMem = CreateCompatibleDC( hdc );
1240 SelectObject( hdcMem, bm );
1241 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1242 check_bitmap_width, check_bitmap_height,
1243 hdcMem, 0, 0, SRCCOPY );
1244 DeleteDC( hdcMem );
1246 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1248 RECT r;
1249 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1250 HDC hdcMem = CreateCompatibleDC( hdc );
1251 SelectObject( hdcMem, bm );
1252 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1253 DrawFrameControl( hdcMem, &r, DFC_MENU,
1254 (lpitem->fType & MFT_RADIOCHECK) ?
1255 DFCS_MENUBULLET : DFCS_MENUCHECK );
1256 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1257 hdcMem, 0, 0, SRCCOPY );
1258 DeleteDC( hdcMem );
1259 DeleteObject( bm );
1263 /* Draw the popup-menu arrow */
1264 if (lpitem->fType & MF_POPUP)
1266 HDC hdcMem = CreateCompatibleDC( hdc );
1267 HBITMAP hOrigBitmap;
1269 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1270 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1271 (y - arrow_bitmap_height) / 2,
1272 arrow_bitmap_width, arrow_bitmap_height,
1273 hdcMem, 0, 0, SRCCOPY );
1274 SelectObject( hdcMem, hOrigBitmap );
1275 DeleteDC( hdcMem );
1278 rect.left += check_bitmap_width;
1279 rect.right -= arrow_bitmap_width;
1282 /* Done for owner-drawn */
1283 if (lpitem->fType & MF_OWNERDRAW)
1284 return;
1286 /* Draw the item text or bitmap */
1287 if (IS_BITMAP_ITEM(lpitem->fType))
1289 int left,top,w,h;
1290 DWORD rop;
1292 HBITMAP resBmp = 0;
1294 HDC hdcMem = CreateCompatibleDC( hdc );
1297 * Check if there is a magic menu item associated with this item
1298 * and load the appropriate bitmap
1300 if (IS_MAGIC_ITEM(lpitem->text))
1302 resBmp = MENU_LoadMagicItem((int)lpitem->text, (lpitem->fState & MF_HILITE),
1303 lpitem->dwItemData);
1305 else
1306 resBmp = (HBITMAP)lpitem->text;
1308 if (resBmp)
1310 BITMAP bm;
1311 GetObjectA( resBmp, sizeof(bm), &bm );
1313 SelectObject(hdcMem,resBmp );
1315 /* handle fontsize > bitmap_height */
1316 h=rect.bottom - rect.top;
1317 top = (h>bm.bmHeight) ?
1318 rect.top+(h-bm.bmHeight)/2 : rect.top;
1319 w=rect.right - rect.left;
1320 left=rect.left;
1321 if (TWEAK_WineLook == WIN95_LOOK) {
1322 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
1323 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
1324 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1325 } else {
1326 left++;
1327 w-=2;
1328 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
1330 BitBlt( hdc, left, top, w,
1331 h, hdcMem, 0, 0,
1332 rop);
1334 DeleteDC( hdcMem );
1336 return;
1339 /* No bitmap - process text if present */
1340 else if (IS_STRING_ITEM(lpitem->fType))
1342 register int i;
1343 HFONT hfontOld = 0;
1345 UINT uFormat = (menuBar) ?
1346 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1347 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1349 if ( lpitem->fState & MFS_DEFAULT )
1351 hfontOld = SelectObject( hdc, hMenuFontBold);
1354 if (menuBar)
1356 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1357 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1358 i = strlenW( lpitem->text );
1360 else
1362 for (i = 0; lpitem->text[i]; i++)
1363 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1364 break;
1367 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1369 if (!(lpitem->fState & MF_HILITE) )
1371 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1372 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1373 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1374 --rect.left; --rect.top; --rect.right; --rect.bottom;
1376 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1379 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1381 /* paint the shortcut text */
1382 if (lpitem->text[i]) /* There's a tab or flush-right char */
1384 if (lpitem->text[i] == '\t')
1386 rect.left = lpitem->xTab;
1387 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1389 else
1391 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1394 if( !(TWEAK_WineLook == WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1396 if (!(lpitem->fState & MF_HILITE) )
1398 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1399 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1400 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1401 --rect.left; --rect.top; --rect.right; --rect.bottom;
1403 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1405 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1408 if (hfontOld)
1409 SelectObject (hdc, hfontOld);
1414 /***********************************************************************
1415 * MENU_DrawPopupMenu
1417 * Paint a popup menu.
1419 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1421 HBRUSH hPrevBrush = 0;
1422 RECT rect;
1424 TRACE("wnd=0x%04x dc=0x%04x menu=0x%04x\n", hwnd, hdc, hmenu);
1426 GetClientRect( hwnd, &rect );
1428 if(TWEAK_WineLook == WIN31_LOOK)
1430 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1431 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1434 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1435 && (SelectObject( hdc, hMenuFont)))
1437 HPEN hPrevPen;
1439 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1441 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1442 if( hPrevPen )
1444 INT ropPrev, i;
1445 POPUPMENU *menu;
1447 /* draw 3-d shade */
1448 if(TWEAK_WineLook == WIN31_LOOK) {
1449 SelectObject( hdc, hShadeBrush );
1450 SetBkMode( hdc, TRANSPARENT );
1451 ropPrev = SetROP2( hdc, R2_MASKPEN );
1453 i = rect.right; /* why SetBrushOrg() doesn't? */
1454 PatBlt( hdc, i & 0xfffffffe,
1455 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1456 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1457 rect.bottom - rect.top, 0x00a000c9 );
1458 i = rect.bottom;
1459 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1460 i & 0xfffffffe,rect.right - rect.left,
1461 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1462 SelectObject( hdc, hPrevPen );
1463 SelectObject( hdc, hPrevBrush );
1464 SetROP2( hdc, ropPrev );
1466 else
1467 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1469 /* draw menu items */
1471 menu = MENU_GetMenu( hmenu );
1472 if (menu && menu->nItems)
1474 MENUITEM *item;
1475 UINT u;
1477 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1478 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1479 menu->Height, FALSE, ODA_DRAWENTIRE );
1482 } else
1484 SelectObject( hdc, hPrevBrush );
1489 /***********************************************************************
1490 * MENU_DrawMenuBar
1492 * Paint a menu bar. Returns the height of the menu bar.
1493 * called from [windows/nonclient.c]
1495 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1496 BOOL suppress_draw)
1498 LPPOPUPMENU lppop;
1499 UINT i,retvalue;
1500 HFONT hfontOld = 0;
1502 WND *wndPtr = WIN_FindWndPtr( hwnd );
1504 lppop = MENU_GetMenu ((HMENU)wndPtr->wIDmenu );
1505 if (lppop == NULL || lprect == NULL)
1507 retvalue = GetSystemMetrics(SM_CYMENU);
1508 goto END;
1511 TRACE("(%04x, %p, %p)\n", hDC, lprect, lppop);
1513 hfontOld = SelectObject( hDC, hMenuFont);
1515 if (lppop->Height == 0)
1516 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1518 lprect->bottom = lprect->top + lppop->Height;
1520 if (suppress_draw)
1522 retvalue = lppop->Height;
1523 goto END;
1526 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
1528 if (TWEAK_WineLook == WIN31_LOOK)
1530 SelectObject( hDC, GetSysColorPen(COLOR_WINDOWFRAME) );
1531 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1532 LineTo( hDC, lprect->right, lprect->bottom );
1534 else
1536 SelectObject( hDC, GetSysColorPen(COLOR_3DFACE));
1537 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
1538 LineTo( hDC, lprect->right, lprect->bottom );
1541 if (lppop->nItems == 0)
1543 retvalue = GetSystemMetrics(SM_CYMENU);
1544 goto END;
1547 for (i = 0; i < lppop->nItems; i++)
1549 MENU_DrawMenuItem( hwnd, (HMENU)wndPtr->wIDmenu, hwnd,
1550 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
1552 retvalue = lppop->Height;
1554 END:
1555 if (hfontOld)
1556 SelectObject (hDC, hfontOld);
1558 WIN_ReleaseWndPtr(wndPtr);
1559 return retvalue;
1562 /***********************************************************************
1563 * MENU_PatchResidentPopup
1565 BOOL MENU_PatchResidentPopup( HQUEUE16 checkQueue, WND* checkWnd )
1567 WND *pTPWnd = MENU_GetTopPopupWnd();
1569 if( pTPWnd )
1571 HTASK16 hTask = 0;
1573 TRACE("patching resident popup: %04x %04x [%04x %04x]\n",
1574 checkQueue, checkWnd ? checkWnd->hwndSelf : 0, pTPWnd->hmemTaskQ,
1575 pTPWnd->owner ? pTPWnd->owner->hwndSelf : 0);
1577 switch( checkQueue )
1579 case 0: /* checkWnd is the new popup owner */
1580 if( checkWnd )
1582 pTPWnd->owner = checkWnd;
1583 if( pTPWnd->hmemTaskQ != checkWnd->hmemTaskQ )
1584 hTask = QUEUE_GetQueueTask( checkWnd->hmemTaskQ );
1586 break;
1588 case 0xFFFF: /* checkWnd is destroyed */
1589 if( pTPWnd->owner == checkWnd )
1590 pTPWnd->owner = NULL;
1591 MENU_ReleaseTopPopupWnd();
1592 return TRUE;
1594 default: /* checkQueue is exiting */
1595 if( pTPWnd->hmemTaskQ == checkQueue )
1597 hTask = QUEUE_GetQueueTask( pTPWnd->hmemTaskQ );
1598 hTask = TASK_GetNextTask( hTask );
1600 break;
1603 if( hTask )
1605 TDB* task = (TDB*)GlobalLock16( hTask );
1606 if( task )
1608 pTPWnd->hInstance = task->hInstance;
1609 pTPWnd->hmemTaskQ = task->hQueue;
1610 MENU_ReleaseTopPopupWnd();
1611 return TRUE;
1613 else WARN("failed to patch resident popup.\n");
1616 MENU_ReleaseTopPopupWnd();
1617 return FALSE;
1620 /***********************************************************************
1621 * MENU_ShowPopup
1623 * Display a popup menu.
1625 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1626 INT x, INT y, INT xanchor, INT yanchor )
1628 POPUPMENU *menu;
1629 WND *wndOwner = NULL;
1631 TRACE("owner=0x%04x hmenu=0x%04x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1632 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1634 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1635 if (menu->FocusedItem != NO_SELECTED_ITEM)
1637 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1638 menu->FocusedItem = NO_SELECTED_ITEM;
1641 /* store the owner for DrawItem */
1642 menu->hwndOwner = hwndOwner;
1644 if( (wndOwner = WIN_FindWndPtr( hwndOwner )) )
1646 UINT width, height;
1648 MENU_PopupMenuCalcSize( menu, hwndOwner );
1650 /* adjust popup menu pos so that it fits within the desktop */
1652 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1653 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1655 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1657 if( xanchor )
1658 x -= width - xanchor;
1659 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1660 x = GetSystemMetrics(SM_CXSCREEN) - width;
1662 if( x < 0 ) x = 0;
1664 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1666 if( yanchor )
1667 y -= height + yanchor;
1668 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1669 y = GetSystemMetrics(SM_CYSCREEN) - height;
1671 if( y < 0 ) y = 0;
1673 if( TWEAK_WineLook == WIN31_LOOK )
1675 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1676 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1679 /* NOTE: In Windows, top menu popup is not owned. */
1680 if (!pTopPopupWnd) /* create top level popup menu window */
1682 assert( uSubPWndLevel == 0 );
1684 pTopPopupWnd = WIN_FindWndPtr(CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1685 WS_POPUP, x, y, width, height,
1686 hwndOwner, 0, wndOwner->hInstance,
1687 (LPVOID)hmenu ));
1688 if (!pTopPopupWnd)
1690 WIN_ReleaseWndPtr(wndOwner);
1691 return FALSE;
1693 menu->hWnd = pTopPopupWnd->hwndSelf;
1694 MENU_ReleaseTopPopupWnd();
1696 else
1697 if( uSubPWndLevel )
1699 /* create a new window for the submenu */
1701 menu->hWnd = CreateWindowA( POPUPMENU_CLASS_ATOM, NULL,
1702 WS_POPUP, x, y, width, height,
1703 hwndOwner, 0, wndOwner->hInstance,
1704 (LPVOID)hmenu );
1705 if( !menu->hWnd )
1707 WIN_ReleaseWndPtr(wndOwner);
1708 return FALSE;
1711 else /* top level popup menu window already exists */
1713 WND *pTPWnd = MENU_GetTopPopupWnd();
1714 menu->hWnd = pTPWnd->hwndSelf;
1716 MENU_PatchResidentPopup( 0, wndOwner );
1717 SendMessageA( pTPWnd->hwndSelf, MM_SETMENUHANDLE, (WPARAM16)hmenu, 0L);
1719 /* adjust its size */
1721 SetWindowPos( menu->hWnd, 0, x, y, width, height,
1722 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW);
1723 MENU_ReleaseTopPopupWnd();
1726 uSubPWndLevel++; /* menu level counter */
1728 /* Display the window */
1730 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1731 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1732 UpdateWindow( menu->hWnd );
1733 WIN_ReleaseWndPtr(wndOwner);
1734 return TRUE;
1736 return FALSE;
1740 /***********************************************************************
1741 * MENU_SelectItem
1743 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1744 BOOL sendMenuSelect, HMENU topmenu )
1746 LPPOPUPMENU lppop;
1747 HDC hdc;
1749 TRACE("owner=0x%04x menu=0x%04x index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1751 lppop = MENU_GetMenu( hmenu );
1752 if ((!lppop) || (!lppop->nItems)) return;
1754 if (lppop->FocusedItem == wIndex) return;
1755 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1756 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1758 SelectObject( hdc, hMenuFont);
1760 /* Clear previous highlighted item */
1761 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1763 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1764 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1765 lppop->Height, !(lppop->wFlags & MF_POPUP),
1766 ODA_SELECT );
1769 /* Highlight new item (if any) */
1770 lppop->FocusedItem = wIndex;
1771 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1773 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1774 lppop->items[wIndex].fState |= MF_HILITE;
1775 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1776 &lppop->items[wIndex], lppop->Height,
1777 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1779 if (sendMenuSelect)
1781 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1782 SendMessageA( hwndOwner, WM_MENUSELECT,
1783 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1784 ip->fType | ip->fState | MF_MOUSESELECT |
1785 (lppop->wFlags & MF_SYSMENU)), hmenu);
1788 else if (sendMenuSelect) {
1789 if(topmenu){
1790 int pos;
1791 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1792 POPUPMENU *ptm = (POPUPMENU *) USER_HEAP_LIN_ADDR( topmenu );
1793 MENUITEM *ip = &ptm->items[pos];
1794 SendMessageA( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1795 ip->fType | ip->fState | MF_MOUSESELECT |
1796 (ptm->wFlags & MF_SYSMENU)), topmenu);
1800 ReleaseDC( lppop->hWnd, hdc );
1804 /***********************************************************************
1805 * MENU_MoveSelection
1807 * Moves currently selected item according to the offset parameter.
1808 * If there is no selection then it should select the last item if
1809 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1811 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1813 INT i;
1814 POPUPMENU *menu;
1816 TRACE("hwnd=0x%04x hmenu=0x%04x off=0x%04x\n", hwndOwner, hmenu, offset);
1818 menu = MENU_GetMenu( hmenu );
1819 if ((!menu) || (!menu->items)) return;
1821 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1823 if( menu->nItems == 1 ) return; else
1824 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1825 ; i += offset)
1826 if (!(menu->items[i].fType & MF_SEPARATOR))
1828 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1829 return;
1833 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1834 i >= 0 && i < menu->nItems ; i += offset)
1835 if (!(menu->items[i].fType & MF_SEPARATOR))
1837 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1838 return;
1843 /**********************************************************************
1844 * MENU_SetItemData
1846 * Set an item flags, id and text ptr. Called by InsertMenu() and
1847 * ModifyMenu().
1849 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT id,
1850 LPCWSTR str )
1852 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1854 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1856 if (IS_STRING_ITEM(flags))
1858 if (!str)
1860 flags |= MF_SEPARATOR;
1861 item->text = NULL;
1863 else
1865 LPWSTR text;
1866 /* Item beginning with a backspace is a help item */
1867 if (*str == '\b')
1869 flags |= MF_HELP;
1870 str++;
1872 if (!(text = HEAP_strdupW( SystemHeap, 0, str ))) return FALSE;
1873 item->text = text;
1876 else if (IS_BITMAP_ITEM(flags))
1877 item->text = (LPWSTR)(HBITMAP)LOWORD(str);
1878 else item->text = NULL;
1880 if (flags & MF_OWNERDRAW)
1881 item->dwItemData = (DWORD)str;
1882 else
1883 item->dwItemData = 0;
1885 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != id) )
1886 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1888 if (flags & MF_POPUP)
1890 POPUPMENU *menu = MENU_GetMenu((UINT16)id);
1891 if (menu) menu->wFlags |= MF_POPUP;
1892 else
1894 item->wID = 0;
1895 item->hSubMenu = 0;
1896 item->fType = 0;
1897 item->fState = 0;
1898 return FALSE;
1902 item->wID = id;
1903 if (flags & MF_POPUP)
1904 item->hSubMenu = id;
1906 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1907 flags |= MF_POPUP; /* keep popup */
1909 item->fType = flags & TYPE_MASK;
1910 item->fState = (flags & STATE_MASK) &
1911 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1914 /* Don't call SetRectEmpty here! */
1917 if (prevText) HeapFree( SystemHeap, 0, prevText );
1919 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1920 return TRUE;
1924 /**********************************************************************
1925 * MENU_InsertItem
1927 * Insert a new item into a menu.
1929 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1931 MENUITEM *newItems;
1932 POPUPMENU *menu;
1934 if (!(menu = MENU_GetMenu(hMenu)))
1935 return NULL;
1937 /* Find where to insert new item */
1939 if (flags & MF_BYPOSITION) {
1940 if (pos > menu->nItems)
1941 pos = menu->nItems;
1942 } else {
1943 if (!MENU_FindItem( &hMenu, &pos, flags ))
1944 pos = menu->nItems;
1945 else {
1946 if (!(menu = MENU_GetMenu( hMenu )))
1947 return NULL;
1951 /* Create new items array */
1953 newItems = HeapAlloc( SystemHeap, 0, sizeof(MENUITEM) * (menu->nItems+1) );
1954 if (!newItems)
1956 WARN("allocation failed\n" );
1957 return NULL;
1959 if (menu->nItems > 0)
1961 /* Copy the old array into the new one */
1962 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1963 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1964 (menu->nItems-pos)*sizeof(MENUITEM) );
1965 HeapFree( SystemHeap, 0, menu->items );
1967 menu->items = newItems;
1968 menu->nItems++;
1969 memset( &newItems[pos], 0, sizeof(*newItems) );
1970 menu->Height = 0; /* force size recalculate */
1971 return &newItems[pos];
1975 /**********************************************************************
1976 * MENU_ParseResource
1978 * Parse a standard menu resource and add items to the menu.
1979 * Return a pointer to the end of the resource.
1981 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1983 WORD flags, id = 0;
1984 LPCSTR str;
1988 flags = GET_WORD(res);
1989 res += sizeof(WORD);
1990 if (!(flags & MF_POPUP))
1992 id = GET_WORD(res);
1993 res += sizeof(WORD);
1995 if (!IS_STRING_ITEM(flags))
1996 ERR("not a string item %04x\n", flags );
1997 str = res;
1998 if (!unicode) res += strlen(str) + 1;
1999 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2000 if (flags & MF_POPUP)
2002 HMENU hSubMenu = CreatePopupMenu();
2003 if (!hSubMenu) return NULL;
2004 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2005 return NULL;
2006 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
2007 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
2009 else /* Not a popup */
2011 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2012 else AppendMenuW( hMenu, flags, id,
2013 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2015 } while (!(flags & MF_END));
2016 return res;
2020 /**********************************************************************
2021 * MENUEX_ParseResource
2023 * Parse an extended menu resource and add items to the menu.
2024 * Return a pointer to the end of the resource.
2026 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2028 WORD resinfo;
2029 do {
2030 MENUITEMINFOW mii;
2032 mii.cbSize = sizeof(mii);
2033 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2034 mii.fType = GET_DWORD(res);
2035 res += sizeof(DWORD);
2036 mii.fState = GET_DWORD(res);
2037 res += sizeof(DWORD);
2038 mii.wID = GET_DWORD(res);
2039 res += sizeof(DWORD);
2040 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2041 res += sizeof(WORD);
2042 /* Align the text on a word boundary. */
2043 res += (~((int)res - 1)) & 1;
2044 mii.dwTypeData = (LPWSTR) res;
2045 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2046 /* Align the following fields on a dword boundary. */
2047 res += (~((int)res - 1)) & 3;
2049 /* FIXME: This is inefficient and cannot be optimised away by gcc. */
2051 LPSTR newstr = HEAP_strdupWtoA(GetProcessHeap(),
2052 0, mii.dwTypeData);
2053 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2054 mii.fType, mii.fState, mii.wID, resinfo, newstr);
2055 HeapFree( GetProcessHeap(), 0, newstr );
2058 if (resinfo & 1) { /* Pop-up? */
2059 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2060 res += sizeof(DWORD);
2061 mii.hSubMenu = CreatePopupMenu();
2062 if (!mii.hSubMenu)
2063 return NULL;
2064 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2065 DestroyMenu(mii.hSubMenu);
2066 return NULL;
2068 mii.fMask |= MIIM_SUBMENU;
2069 mii.fType |= MF_POPUP;
2071 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2072 } while (!(resinfo & MF_END));
2073 return res;
2077 /***********************************************************************
2078 * MENU_GetSubPopup
2080 * Return the handle of the selected sub-popup menu (if any).
2082 static HMENU MENU_GetSubPopup( HMENU hmenu )
2084 POPUPMENU *menu;
2085 MENUITEM *item;
2087 menu = MENU_GetMenu( hmenu );
2089 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2091 item = &menu->items[menu->FocusedItem];
2092 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2093 return item->hSubMenu;
2094 return 0;
2098 /***********************************************************************
2099 * MENU_HideSubPopups
2101 * Hide the sub-popup menus of this menu.
2103 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2104 BOOL sendMenuSelect )
2106 POPUPMENU *menu = MENU_GetMenu( hmenu );
2108 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2110 if (menu && uSubPWndLevel)
2112 HMENU hsubmenu;
2113 POPUPMENU *submenu;
2114 MENUITEM *item;
2116 if (menu->FocusedItem != NO_SELECTED_ITEM)
2118 item = &menu->items[menu->FocusedItem];
2119 if (!(item->fType & MF_POPUP) ||
2120 !(item->fState & MF_MOUSESELECT)) return;
2121 item->fState &= ~MF_MOUSESELECT;
2122 hsubmenu = item->hSubMenu;
2123 } else return;
2125 submenu = MENU_GetMenu( hsubmenu );
2126 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2127 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2129 if (submenu->hWnd == MENU_GetTopPopupWnd()->hwndSelf )
2131 ShowWindow( submenu->hWnd, SW_HIDE );
2132 uSubPWndLevel = 0;
2134 else
2136 DestroyWindow( submenu->hWnd );
2137 submenu->hWnd = 0;
2139 MENU_ReleaseTopPopupWnd();
2144 /***********************************************************************
2145 * MENU_ShowSubPopup
2147 * Display the sub-menu of the selected item of this menu.
2148 * Return the handle of the submenu, or hmenu if no submenu to display.
2150 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2151 BOOL selectFirst, UINT wFlags )
2153 RECT rect;
2154 POPUPMENU *menu;
2155 MENUITEM *item;
2156 WND *wndPtr;
2157 HDC hdc;
2159 TRACE("owner=0x%04x hmenu=0x%04x 0x%04x\n", hwndOwner, hmenu, selectFirst);
2161 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2163 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd )) ||
2164 (menu->FocusedItem == NO_SELECTED_ITEM))
2166 WIN_ReleaseWndPtr(wndPtr);
2167 return hmenu;
2170 item = &menu->items[menu->FocusedItem];
2171 if (!(item->fType & MF_POPUP) ||
2172 (item->fState & (MF_GRAYED | MF_DISABLED)))
2174 WIN_ReleaseWndPtr(wndPtr);
2175 return hmenu;
2178 /* message must be sent before using item,
2179 because nearly everything may be changed by the application ! */
2181 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2182 if (!(wFlags & TPM_NONOTIFY))
2183 SendMessageA( hwndOwner, WM_INITMENUPOPUP, item->hSubMenu,
2184 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2186 item = &menu->items[menu->FocusedItem];
2187 rect = item->rect;
2189 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2190 if (!(item->fState & MF_HILITE))
2192 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2193 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2195 SelectObject( hdc, hMenuFont);
2197 item->fState |= MF_HILITE;
2198 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2199 ReleaseDC( menu->hWnd, hdc );
2201 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2202 item->rect = rect;
2204 item->fState |= MF_MOUSESELECT;
2206 if (IS_SYSTEM_MENU(menu))
2208 MENU_InitSysMenuPopup(item->hSubMenu, wndPtr->dwStyle, GetClassLongA(wndPtr->hwndSelf, GCL_STYLE));
2210 NC_GetSysPopupPos( wndPtr, &rect );
2211 rect.top = rect.bottom;
2212 rect.right = GetSystemMetrics(SM_CXSIZE);
2213 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2215 else
2217 if (menu->wFlags & MF_POPUP)
2219 rect.left = wndPtr->rectWindow.left + item->rect.right - GetSystemMetrics(SM_CXBORDER);
2220 rect.top = wndPtr->rectWindow.top + item->rect.top;
2221 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2222 rect.bottom = item->rect.top - item->rect.bottom;
2224 else
2226 rect.left = wndPtr->rectWindow.left + item->rect.left;
2227 rect.top = wndPtr->rectWindow.top + item->rect.bottom;
2228 rect.right = item->rect.right - item->rect.left;
2229 rect.bottom = item->rect.bottom - item->rect.top;
2233 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2234 rect.left, rect.top, rect.right, rect.bottom );
2235 if (selectFirst)
2236 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2237 WIN_ReleaseWndPtr(wndPtr);
2238 return item->hSubMenu;
2243 /**********************************************************************
2244 * MENU_IsMenuActive
2246 BOOL MENU_IsMenuActive(void)
2248 return pTopPopupWnd && (pTopPopupWnd->dwStyle & WS_VISIBLE);
2251 /***********************************************************************
2252 * MENU_PtMenu
2254 * Walks menu chain trying to find a menu pt maps to.
2256 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2258 POPUPMENU *menu = MENU_GetMenu( hMenu );
2259 register UINT ht = menu->FocusedItem;
2261 /* try subpopup first (if any) */
2262 ht = (ht != NO_SELECTED_ITEM &&
2263 (menu->items[ht].fType & MF_POPUP) &&
2264 (menu->items[ht].fState & MF_MOUSESELECT))
2265 ? (UINT) MENU_PtMenu(menu->items[ht].hSubMenu, pt) : 0;
2267 if( !ht ) /* check the current window (avoiding WM_HITTEST) */
2269 ht = (UINT)NC_HandleNCHitTest( menu->hWnd, pt );
2270 if( menu->wFlags & MF_POPUP )
2271 ht = (ht != (UINT)HTNOWHERE &&
2272 ht != (UINT)HTERROR) ? (UINT)hMenu : 0;
2273 else
2275 WND* wndPtr = WIN_FindWndPtr(menu->hWnd);
2277 ht = ( ht == HTSYSMENU ) ? (UINT)(wndPtr->hSysMenu)
2278 : ( ht == HTMENU ) ? (UINT)(wndPtr->wIDmenu) : 0;
2279 WIN_ReleaseWndPtr(wndPtr);
2282 return (HMENU)ht;
2285 /***********************************************************************
2286 * MENU_ExecFocusedItem
2288 * Execute a menu item (for instance when user pressed Enter).
2289 * Return the wID of the executed item. Otherwise, -1 indicating
2290 * that no menu item was executed;
2291 * Have to receive the flags for the TrackPopupMenu options to avoid
2292 * sending unwanted message.
2295 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2297 MENUITEM *item;
2298 POPUPMENU *menu = MENU_GetMenu( hMenu );
2300 TRACE("%p hmenu=0x%04x\n", pmt, hMenu);
2302 if (!menu || !menu->nItems ||
2303 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2305 item = &menu->items[menu->FocusedItem];
2307 TRACE("%08x %08x %08x\n",
2308 hMenu, item->wID, item->hSubMenu);
2310 if (!(item->fType & MF_POPUP))
2312 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2314 /* If TPM_RETURNCMD is set you return the id, but
2315 do not send a message to the owner */
2316 if(!(wFlags & TPM_RETURNCMD))
2318 if( menu->wFlags & MF_SYSMENU )
2319 PostMessageA( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2320 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2321 else
2322 PostMessageA( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2324 return item->wID;
2327 else
2328 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2330 return -1;
2333 /***********************************************************************
2334 * MENU_SwitchTracking
2336 * Helper function for menu navigation routines.
2338 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2340 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2341 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2343 TRACE("%p hmenu=0x%04x 0x%04x\n", pmt, hPtMenu, id);
2345 if( pmt->hTopMenu != hPtMenu &&
2346 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2348 /* both are top level menus (system and menu-bar) */
2349 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2350 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2351 pmt->hTopMenu = hPtMenu;
2353 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2354 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2358 /***********************************************************************
2359 * MENU_ButtonDown
2361 * Return TRUE if we can go on with menu tracking.
2363 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2365 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2367 if (hPtMenu)
2369 UINT id = 0;
2370 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2371 MENUITEM *item;
2373 if( IS_SYSTEM_MENU(ptmenu) )
2374 item = ptmenu->items;
2375 else
2376 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2378 if( item )
2380 if( ptmenu->FocusedItem != id )
2381 MENU_SwitchTracking( pmt, hPtMenu, id );
2383 /* If the popup menu is not already "popped" */
2384 if(!(item->fState & MF_MOUSESELECT ))
2386 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2388 /* In win31, a newly popped menu always remains opened for the next buttonup */
2389 if(TWEAK_WineLook == WIN31_LOOK)
2390 ptmenu->bTimeToHide = FALSE;
2393 return TRUE;
2395 /* Else the click was on the menu bar, finish the tracking */
2397 return FALSE;
2400 /***********************************************************************
2401 * MENU_ButtonUp
2403 * Return the value of MENU_ExecFocusedItem if
2404 * the selected item was not a popup. Else open the popup.
2405 * A -1 return value indicates that we go on with menu tracking.
2408 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2410 TRACE("%p hmenu=0x%04x\n", pmt, hPtMenu);
2412 if (hPtMenu)
2414 UINT id = 0;
2415 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2416 MENUITEM *item;
2418 if( IS_SYSTEM_MENU(ptmenu) )
2419 item = ptmenu->items;
2420 else
2421 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2423 if( item && (ptmenu->FocusedItem == id ))
2425 if( !(item->fType & MF_POPUP) )
2426 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2428 /* If we are dealing with the top-level menu */
2429 /* and this is a click on an already "popped" item: */
2430 /* Stop the menu tracking and close the opened submenus */
2431 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2432 return 0;
2434 ptmenu->bTimeToHide = TRUE;
2436 return -1;
2440 /***********************************************************************
2441 * MENU_MouseMove
2443 * Return TRUE if we can go on with menu tracking.
2445 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2447 UINT id = NO_SELECTED_ITEM;
2448 POPUPMENU *ptmenu = NULL;
2450 if( hPtMenu )
2452 ptmenu = MENU_GetMenu( hPtMenu );
2453 if( IS_SYSTEM_MENU(ptmenu) )
2454 id = 0;
2455 else
2456 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2459 if( id == NO_SELECTED_ITEM )
2461 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2462 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2465 else if( ptmenu->FocusedItem != id )
2467 MENU_SwitchTracking( pmt, hPtMenu, id );
2468 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2470 return TRUE;
2474 /***********************************************************************
2475 * MENU_DoNextMenu
2477 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2479 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2481 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2483 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2484 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2486 WND* wndPtr;
2487 HMENU hNewMenu;
2488 HWND hNewWnd;
2489 UINT id = 0;
2490 LRESULT l = SendMessageA( pmt->hOwnerWnd, WM_NEXTMENU, vk,
2491 (IS_SYSTEM_MENU(menu)) ? GetSubMenu16(pmt->hTopMenu,0) : pmt->hTopMenu );
2493 TRACE("%04x [%04x] -> %04x [%04x]\n",
2494 (UINT16)pmt->hCurrentMenu, (UINT16)pmt->hOwnerWnd, LOWORD(l), HIWORD(l) );
2496 if( l == 0 )
2498 wndPtr = WIN_FindWndPtr(pmt->hOwnerWnd);
2500 hNewWnd = pmt->hOwnerWnd;
2501 if( IS_SYSTEM_MENU(menu) )
2503 /* switch to the menu bar */
2505 if( wndPtr->dwStyle & WS_CHILD || !wndPtr->wIDmenu )
2507 WIN_ReleaseWndPtr(wndPtr);
2508 return FALSE;
2511 hNewMenu = wndPtr->wIDmenu;
2512 if( vk == VK_LEFT )
2514 menu = MENU_GetMenu( hNewMenu );
2515 id = menu->nItems - 1;
2518 else if( wndPtr->dwStyle & WS_SYSMENU )
2520 /* switch to the system menu */
2521 hNewMenu = wndPtr->hSysMenu;
2523 else
2525 WIN_ReleaseWndPtr(wndPtr);
2526 return FALSE;
2528 WIN_ReleaseWndPtr(wndPtr);
2530 else /* application returned a new menu to switch to */
2532 hNewMenu = LOWORD(l); hNewWnd = HIWORD(l);
2534 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2536 wndPtr = WIN_FindWndPtr(hNewWnd);
2538 if( wndPtr->dwStyle & WS_SYSMENU &&
2539 GetSubMenu16(wndPtr->hSysMenu, 0) == hNewMenu )
2541 /* get the real system menu */
2542 hNewMenu = wndPtr->hSysMenu;
2544 else if( wndPtr->dwStyle & WS_CHILD || wndPtr->wIDmenu != hNewMenu )
2546 /* FIXME: Not sure what to do here;
2547 * perhaps try to track hNewMenu as a popup? */
2549 TRACE(" -- got confused.\n");
2550 WIN_ReleaseWndPtr(wndPtr);
2551 return FALSE;
2553 WIN_ReleaseWndPtr(wndPtr);
2555 else return FALSE;
2558 if( hNewMenu != pmt->hTopMenu )
2560 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2561 FALSE, 0 );
2562 if( pmt->hCurrentMenu != pmt->hTopMenu )
2563 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2566 if( hNewWnd != pmt->hOwnerWnd )
2568 ReleaseCapture();
2569 pmt->hOwnerWnd = hNewWnd;
2570 EVENT_Capture( pmt->hOwnerWnd, HTMENU );
2573 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2574 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2576 return TRUE;
2578 return FALSE;
2581 /***********************************************************************
2582 * MENU_SuspendPopup
2584 * The idea is not to show the popup if the next input message is
2585 * going to hide it anyway.
2587 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2589 MSG msg;
2591 msg.hwnd = pmt->hOwnerWnd;
2593 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2594 pmt->trackFlags |= TF_SKIPREMOVE;
2596 switch( uMsg )
2598 case WM_KEYDOWN:
2599 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2600 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2602 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2603 PeekMessageA( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2604 if( msg.message == WM_KEYDOWN &&
2605 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2607 pmt->trackFlags |= TF_SUSPENDPOPUP;
2608 return TRUE;
2611 break;
2614 /* failures go through this */
2615 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2616 return FALSE;
2619 /***********************************************************************
2620 * MENU_KeyLeft
2622 * Handle a VK_LEFT key event in a menu.
2624 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2626 POPUPMENU *menu;
2627 HMENU hmenutmp, hmenuprev;
2628 UINT prevcol;
2630 hmenuprev = hmenutmp = pmt->hTopMenu;
2631 menu = MENU_GetMenu( hmenutmp );
2633 /* Try to move 1 column left (if possible) */
2634 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2635 NO_SELECTED_ITEM ) {
2637 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2638 prevcol, TRUE, 0 );
2639 return;
2642 /* close topmost popup */
2643 while (hmenutmp != pmt->hCurrentMenu)
2645 hmenuprev = hmenutmp;
2646 hmenutmp = MENU_GetSubPopup( hmenuprev );
2649 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2650 pmt->hCurrentMenu = hmenuprev;
2652 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2654 /* move menu bar selection if no more popups are left */
2656 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2657 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2659 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2661 /* A sublevel menu was displayed - display the next one
2662 * unless there is another displacement coming up */
2664 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2665 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2666 pmt->hTopMenu, TRUE, wFlags);
2672 /***********************************************************************
2673 * MENU_KeyRight
2675 * Handle a VK_RIGHT key event in a menu.
2677 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2679 HMENU hmenutmp;
2680 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2681 UINT nextcol;
2683 TRACE("MENU_KeyRight called, cur %x (%s), top %x (%s).\n",
2684 pmt->hCurrentMenu,
2685 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->
2686 items[0].text),
2687 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2689 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2691 /* If already displaying a popup, try to display sub-popup */
2693 hmenutmp = pmt->hCurrentMenu;
2694 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2696 /* if subpopup was displayed then we are done */
2697 if (hmenutmp != pmt->hCurrentMenu) return;
2700 /* Check to see if there's another column */
2701 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2702 NO_SELECTED_ITEM ) {
2703 TRACE("Going to %d.\n", nextcol );
2704 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2705 nextcol, TRUE, 0 );
2706 return;
2709 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2711 if( pmt->hCurrentMenu != pmt->hTopMenu )
2713 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2714 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2715 } else hmenutmp = 0;
2717 /* try to move to the next item */
2718 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2719 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2721 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2722 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2723 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2724 pmt->hTopMenu, TRUE, wFlags);
2728 /***********************************************************************
2729 * MENU_TrackMenu
2731 * Menu tracking code.
2733 static INT MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2734 HWND hwnd, const RECT *lprect )
2736 MSG msg;
2737 POPUPMENU *menu;
2738 BOOL fRemove;
2739 INT executedMenuId = -1;
2740 MTRACKER mt;
2741 BOOL enterIdleSent = FALSE;
2743 mt.trackFlags = 0;
2744 mt.hCurrentMenu = hmenu;
2745 mt.hTopMenu = hmenu;
2746 mt.hOwnerWnd = hwnd;
2747 mt.pt.x = x;
2748 mt.pt.y = y;
2750 TRACE("hmenu=0x%04x flags=0x%08x (%d,%d) hwnd=0x%04x (%d,%d)-(%d,%d)\n",
2751 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2752 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2754 fEndMenu = FALSE;
2755 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
2757 if (wFlags & TPM_BUTTONDOWN)
2759 /* Get the result in order to start the tracking or not */
2760 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2761 fEndMenu = !fRemove;
2764 EVENT_Capture( mt.hOwnerWnd, HTMENU );
2766 while (!fEndMenu)
2768 menu = MENU_GetMenu( mt.hCurrentMenu );
2769 if (!menu) /* sometimes happens if I do a window manager close */
2770 break;
2771 msg.hwnd = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2773 /* we have to keep the message in the queue until it's
2774 * clear that menu loop is not over yet. */
2776 if (!MSG_InternalGetMessage( QMSG_WIN32A, &msg, msg.hwnd, mt.hOwnerWnd,
2777 MSGF_MENU, PM_NOREMOVE, !enterIdleSent, &enterIdleSent )) break;
2779 /* check if EndMenu() tried to cancel us, by posting this message */
2780 if(msg.message == WM_CANCELMODE)
2782 /* we are now out of the loop */
2783 fEndMenu = TRUE;
2785 /* remove the message from the queue */
2786 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2788 /* break out of internal loop, ala ESCAPE */
2789 break;
2792 TranslateMessage( &msg );
2793 mt.pt = msg.pt;
2795 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2796 enterIdleSent=FALSE;
2798 fRemove = FALSE;
2799 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2802 * use the mouse coordinates in lParam instead of those in the MSG
2803 * struct to properly handle synthetic messages. lParam coords are
2804 * relative to client area, so they must be converted; since they can
2805 * be negative, we must use SLOWORD/SHIWORD instead of LOWORD/HIWORD.
2807 mt.pt.x = SLOWORD(msg.lParam);
2808 mt.pt.y = SHIWORD(msg.lParam);
2809 ClientToScreen(msg.hwnd,&mt.pt);
2811 /* Find a menu for this mouse event */
2812 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2814 switch(msg.message)
2816 /* no WM_NC... messages in captured state */
2818 case WM_RBUTTONDBLCLK:
2819 case WM_RBUTTONDOWN:
2820 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2821 /* fall through */
2822 case WM_LBUTTONDBLCLK:
2823 case WM_LBUTTONDOWN:
2824 /* If the message belongs to the menu, removes it from the queue */
2825 /* Else, end menu tracking */
2826 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2827 fEndMenu = !fRemove;
2828 break;
2830 case WM_RBUTTONUP:
2831 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2832 /* fall through */
2833 case WM_LBUTTONUP:
2834 /* Check if a menu was selected by the mouse */
2835 if (hmenu)
2837 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2839 /* End the loop if executedMenuId is an item ID */
2840 /* or if the job was done (executedMenuId = 0). */
2841 fEndMenu = fRemove = (executedMenuId != -1);
2843 /* No menu was selected by the mouse */
2844 /* if the function was called by TrackPopupMenu, continue
2845 with the menu tracking. If not, stop it */
2846 else
2847 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2849 break;
2851 case WM_MOUSEMOVE:
2852 /* In win95 winelook, the selected menu item must be changed every time the
2853 mouse moves. In Win31 winelook, the mouse button has to be held down */
2855 if ( (TWEAK_WineLook > WIN31_LOOK) ||
2856 ( (msg.wParam & MK_LBUTTON) ||
2857 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON))) )
2859 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2861 } /* switch(msg.message) - mouse */
2863 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2865 fRemove = TRUE; /* Keyboard messages are always removed */
2866 switch(msg.message)
2868 case WM_KEYDOWN:
2869 switch(msg.wParam)
2871 case VK_HOME:
2872 case VK_END:
2873 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2874 NO_SELECTED_ITEM, FALSE, 0 );
2875 /* fall through */
2876 case VK_UP:
2877 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2878 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2879 break;
2881 case VK_DOWN: /* If on menu bar, pull-down the menu */
2883 menu = MENU_GetMenu( mt.hCurrentMenu );
2884 if (!(menu->wFlags & MF_POPUP))
2885 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2886 else /* otherwise try to move selection */
2887 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2888 break;
2890 case VK_LEFT:
2891 MENU_KeyLeft( &mt, wFlags );
2892 break;
2894 case VK_RIGHT:
2895 MENU_KeyRight( &mt, wFlags );
2896 break;
2898 case VK_ESCAPE:
2899 fEndMenu = TRUE;
2900 break;
2902 case VK_F1:
2904 HELPINFO hi;
2905 hi.cbSize = sizeof(HELPINFO);
2906 hi.iContextType = HELPINFO_MENUITEM;
2907 if (menu->FocusedItem == NO_SELECTED_ITEM)
2908 hi.iCtrlId = 0;
2909 else
2910 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2911 hi.hItemHandle = hmenu;
2912 hi.dwContextId = menu->dwContextHelpID;
2913 hi.MousePos = msg.pt;
2914 SendMessageA(hwnd, WM_HELP, 0, (LPARAM)&hi);
2915 break;
2918 default:
2919 break;
2921 break; /* WM_KEYDOWN */
2923 case WM_SYSKEYDOWN:
2924 switch(msg.wParam)
2926 case VK_MENU:
2927 fEndMenu = TRUE;
2928 break;
2931 break; /* WM_SYSKEYDOWN */
2933 case WM_CHAR:
2935 UINT pos;
2937 if (msg.wParam == '\r' || msg.wParam == ' ')
2939 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2940 fEndMenu = (executedMenuId != -1);
2942 break;
2945 /* Hack to avoid control chars. */
2946 /* We will find a better way real soon... */
2947 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
2949 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2950 LOWORD(msg.wParam), FALSE );
2951 if (pos == (UINT)-2) fEndMenu = TRUE;
2952 else if (pos == (UINT)-1) MessageBeep(0);
2953 else
2955 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2956 TRUE, 0 );
2957 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2958 fEndMenu = (executedMenuId != -1);
2961 break;
2962 } /* switch(msg.message) - kbd */
2964 else
2966 DispatchMessageA( &msg );
2969 if (!fEndMenu) fRemove = TRUE;
2971 /* finally remove message from the queue */
2973 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2974 PeekMessageA( &msg, 0, msg.message, msg.message, PM_REMOVE );
2975 else mt.trackFlags &= ~TF_SKIPREMOVE;
2978 ReleaseCapture();
2980 /* If dropdown is still painted and the close box is clicked on
2981 then the menu will be destroyed as part of the DispatchMessage above.
2982 This will then invalidate the menu handle in mt.hTopMenu. We should
2983 check for this first. */
2984 if( IsMenu( mt.hTopMenu ) )
2986 menu = MENU_GetMenu( mt.hTopMenu );
2988 if( IsWindow( mt.hOwnerWnd ) )
2990 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2992 if (menu && menu->wFlags & MF_POPUP)
2994 ShowWindow( menu->hWnd, SW_HIDE );
2995 uSubPWndLevel = 0;
2997 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2998 SendMessageA( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3001 /* Reset the variable for hiding menu */
3002 if( menu ) menu->bTimeToHide = FALSE;
3005 /* The return value is only used by TrackPopupMenu */
3006 return ((executedMenuId != -1) ? executedMenuId : 0);
3009 /***********************************************************************
3010 * MENU_InitTracking
3012 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3014 TRACE("hwnd=0x%04x hmenu=0x%04x\n", hWnd, hMenu);
3016 HideCaret(0);
3018 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3019 if (!(wFlags & TPM_NONOTIFY))
3020 SendMessageA( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3022 SendMessageA( hWnd, WM_SETCURSOR, hWnd, HTCAPTION );
3024 if (!(wFlags & TPM_NONOTIFY))
3025 SendMessageA( hWnd, WM_INITMENU, hMenu, 0 );
3027 return TRUE;
3029 /***********************************************************************
3030 * MENU_ExitTracking
3032 static BOOL MENU_ExitTracking(HWND hWnd)
3034 TRACE("hwnd=0x%04x\n", hWnd);
3036 SendMessageA( hWnd, WM_EXITMENULOOP, 0, 0 );
3037 ShowCaret(0);
3038 return TRUE;
3041 /***********************************************************************
3042 * MENU_TrackMouseMenuBar
3044 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3046 void MENU_TrackMouseMenuBar( WND* wndPtr, INT ht, POINT pt )
3048 HWND hWnd = wndPtr->hwndSelf;
3049 HMENU hMenu = (ht == HTSYSMENU) ? wndPtr->hSysMenu : wndPtr->wIDmenu;
3050 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3052 TRACE("pwnd=%p ht=0x%04x (%ld,%ld)\n", wndPtr, ht, pt.x, pt.y);
3054 if (IsMenu(hMenu))
3056 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3057 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3058 MENU_ExitTracking(hWnd);
3063 /***********************************************************************
3064 * MENU_TrackKbdMenuBar
3066 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3068 void MENU_TrackKbdMenuBar( WND* wndPtr, UINT wParam, INT vkey)
3070 UINT uItem = NO_SELECTED_ITEM;
3071 HMENU hTrackMenu;
3072 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3074 /* find window that has a menu */
3076 while( wndPtr->dwStyle & WS_CHILD)
3077 if( !(wndPtr = wndPtr->parent) ) return;
3079 /* check if we have to track a system menu */
3081 if( (wndPtr->dwStyle & (WS_CHILD | WS_MINIMIZE)) ||
3082 !wndPtr->wIDmenu || vkey == VK_SPACE )
3084 if( !(wndPtr->dwStyle & WS_SYSMENU) ) return;
3085 hTrackMenu = wndPtr->hSysMenu;
3086 uItem = 0;
3087 wParam |= HTSYSMENU; /* prevent item lookup */
3089 else
3090 hTrackMenu = wndPtr->wIDmenu;
3092 if (IsMenu( hTrackMenu ))
3094 MENU_InitTracking( wndPtr->hwndSelf, hTrackMenu, FALSE, wFlags );
3096 if( vkey && vkey != VK_SPACE )
3098 uItem = MENU_FindItemByKey( wndPtr->hwndSelf, hTrackMenu,
3099 vkey, (wParam & HTSYSMENU) );
3100 if( uItem >= (UINT)(-2) )
3102 if( uItem == (UINT)(-1) ) MessageBeep(0);
3103 hTrackMenu = 0;
3107 if( hTrackMenu )
3109 MENU_SelectItem( wndPtr->hwndSelf, hTrackMenu, uItem, TRUE, 0 );
3111 if( uItem == NO_SELECTED_ITEM )
3112 MENU_MoveSelection( wndPtr->hwndSelf, hTrackMenu, ITEM_NEXT );
3113 else if( vkey )
3114 PostMessageA( wndPtr->hwndSelf, WM_KEYDOWN, VK_DOWN, 0L );
3116 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, wndPtr->hwndSelf, NULL );
3119 MENU_ExitTracking (wndPtr->hwndSelf);
3124 /**********************************************************************
3125 * TrackPopupMenu16 (USER.416)
3127 BOOL16 WINAPI TrackPopupMenu16( HMENU16 hMenu, UINT16 wFlags, INT16 x, INT16 y,
3128 INT16 nReserved, HWND16 hWnd, const RECT16 *lpRect )
3130 RECT r;
3131 if (lpRect)
3132 CONV_RECT16TO32( lpRect, &r );
3133 return TrackPopupMenu( hMenu, wFlags, x, y, nReserved, hWnd,
3134 lpRect ? &r : NULL );
3138 /**********************************************************************
3139 * TrackPopupMenu (USER32.@)
3141 * Like the win32 API, the function return the command ID only if the
3142 * flag TPM_RETURNCMD is on.
3145 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3146 INT nReserved, HWND hWnd, const RECT *lpRect )
3148 BOOL ret = FALSE;
3150 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3152 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3153 if (!(wFlags & TPM_NONOTIFY))
3154 SendMessageA( hWnd, WM_INITMENUPOPUP, hMenu, 0);
3156 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3157 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3158 MENU_ExitTracking(hWnd);
3160 if( (!(wFlags & TPM_RETURNCMD)) && (ret != FALSE) )
3161 ret = 1;
3163 return ret;
3166 /**********************************************************************
3167 * TrackPopupMenuEx (USER32.@)
3169 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3170 HWND hWnd, LPTPMPARAMS lpTpm )
3172 FIXME("not fully implemented\n" );
3173 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3174 lpTpm ? &lpTpm->rcExclude : NULL );
3177 /***********************************************************************
3178 * PopupMenuWndProc
3180 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3182 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3184 TRACE("hwnd=0x%04x msg=0x%04x wp=0x%04x lp=0x%08lx\n",
3185 hwnd, message, wParam, lParam);
3187 switch(message)
3189 case WM_CREATE:
3191 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3192 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3193 return 0;
3196 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3197 return MA_NOACTIVATE;
3199 case WM_PAINT:
3201 PAINTSTRUCT ps;
3202 BeginPaint( hwnd, &ps );
3203 MENU_DrawPopupMenu( hwnd, ps.hdc,
3204 (HMENU)GetWindowLongA( hwnd, 0 ) );
3205 EndPaint( hwnd, &ps );
3206 return 0;
3208 case WM_ERASEBKGND:
3209 return 1;
3211 case WM_DESTROY:
3213 /* zero out global pointer in case resident popup window
3214 * was somehow destroyed. */
3216 if(MENU_GetTopPopupWnd() )
3218 if( hwnd == pTopPopupWnd->hwndSelf )
3220 ERR("resident popup destroyed!\n");
3222 MENU_DestroyTopPopupWnd();
3223 uSubPWndLevel = 0;
3225 else
3226 uSubPWndLevel--;
3227 MENU_ReleaseTopPopupWnd();
3229 break;
3231 case WM_SHOWWINDOW:
3233 if( wParam )
3235 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3237 else
3238 SetWindowLongW( hwnd, 0, 0 );
3239 break;
3241 case MM_SETMENUHANDLE:
3242 SetWindowLongW( hwnd, 0, wParam );
3243 break;
3245 case MM_GETMENUHANDLE:
3246 return GetWindowLongW( hwnd, 0 );
3248 default:
3249 return DefWindowProcW( hwnd, message, wParam, lParam );
3251 return 0;
3255 /***********************************************************************
3256 * MENU_GetMenuBarHeight
3258 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3260 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3261 INT orgX, INT orgY )
3263 HDC hdc;
3264 RECT rectBar;
3265 WND *wndPtr;
3266 LPPOPUPMENU lppop;
3267 UINT retvalue;
3269 TRACE("HWND 0x%x, width %d, at (%d, %d).\n",
3270 hwnd, menubarWidth, orgX, orgY );
3272 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
3273 return 0;
3275 if (!(lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu)))
3277 WIN_ReleaseWndPtr(wndPtr);
3278 return 0;
3281 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3282 SelectObject( hdc, hMenuFont);
3283 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3284 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3285 ReleaseDC( hwnd, hdc );
3286 retvalue = lppop->Height;
3287 WIN_ReleaseWndPtr(wndPtr);
3288 return retvalue;
3292 /*******************************************************************
3293 * ChangeMenu16 (USER.153)
3295 BOOL16 WINAPI ChangeMenu16( HMENU16 hMenu, UINT16 pos, SEGPTR data,
3296 UINT16 id, UINT16 flags )
3298 TRACE("menu=%04x pos=%d data=%08lx id=%04x flags=%04x\n",
3299 hMenu, pos, (DWORD)data, id, flags );
3300 if (flags & MF_APPEND) return AppendMenu16( hMenu, flags & ~MF_APPEND,
3301 id, data );
3303 /* FIXME: Word passes the item id in 'pos' and 0 or 0xffff as id */
3304 /* for MF_DELETE. We should check the parameters for all others */
3305 /* MF_* actions also (anybody got a doc on ChangeMenu?). */
3307 if (flags & MF_DELETE) return DeleteMenu16(hMenu, pos, flags & ~MF_DELETE);
3308 if (flags & MF_CHANGE) return ModifyMenu16(hMenu, pos, flags & ~MF_CHANGE,
3309 id, data );
3310 if (flags & MF_REMOVE) return RemoveMenu16(hMenu,
3311 flags & MF_BYPOSITION ? pos : id,
3312 flags & ~MF_REMOVE );
3313 /* Default: MF_INSERT */
3314 return InsertMenu16( hMenu, pos, flags, id, data );
3318 /*******************************************************************
3319 * ChangeMenuA (USER32.@)
3321 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3322 UINT id, UINT flags )
3324 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3325 hMenu, pos, (DWORD)data, id, flags );
3326 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3327 id, data );
3328 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3329 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3330 id, data );
3331 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3332 flags & MF_BYPOSITION ? pos : id,
3333 flags & ~MF_REMOVE );
3334 /* Default: MF_INSERT */
3335 return InsertMenuA( hMenu, pos, flags, id, data );
3339 /*******************************************************************
3340 * ChangeMenuW (USER32.@)
3342 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3343 UINT id, UINT flags )
3345 TRACE("menu=%08x pos=%d data=%08lx id=%08x flags=%08x\n",
3346 hMenu, pos, (DWORD)data, id, flags );
3347 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3348 id, data );
3349 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3350 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3351 id, data );
3352 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3353 flags & MF_BYPOSITION ? pos : id,
3354 flags & ~MF_REMOVE );
3355 /* Default: MF_INSERT */
3356 return InsertMenuW( hMenu, pos, flags, id, data );
3360 /*******************************************************************
3361 * CheckMenuItem16 (USER.154)
3363 BOOL16 WINAPI CheckMenuItem16( HMENU16 hMenu, UINT16 id, UINT16 flags )
3365 return (BOOL16)CheckMenuItem( hMenu, id, flags );
3369 /*******************************************************************
3370 * CheckMenuItem (USER32.@)
3372 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3374 MENUITEM *item;
3375 DWORD ret;
3377 TRACE("menu=%04x id=%04x flags=%04x\n", hMenu, id, flags );
3378 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3379 ret = item->fState & MF_CHECKED;
3380 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3381 else item->fState &= ~MF_CHECKED;
3382 return ret;
3386 /**********************************************************************
3387 * EnableMenuItem16 (USER.155)
3389 UINT16 WINAPI EnableMenuItem16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3391 return EnableMenuItem( hMenu, wItemID, wFlags );
3395 /**********************************************************************
3396 * EnableMenuItem (USER32.@)
3398 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3400 UINT oldflags;
3401 MENUITEM *item;
3402 POPUPMENU *menu;
3404 TRACE("(%04x, %04X, %04X) !\n",
3405 hMenu, wItemID, wFlags);
3407 /* Get the Popupmenu to access the owner menu */
3408 if (!(menu = MENU_GetMenu(hMenu)))
3409 return (UINT)-1;
3411 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3412 return (UINT)-1;
3414 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3415 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3417 /* In win95 if the close item in the system menu change update the close button */
3418 if (TWEAK_WineLook == WIN95_LOOK)
3419 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3421 if (menu->hSysMenuOwner != 0)
3423 POPUPMENU* parentMenu;
3425 /* Get the parent menu to access*/
3426 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3427 return (UINT)-1;
3429 /* Refresh the frame to reflect the change*/
3430 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3431 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3435 return oldflags;
3439 /*******************************************************************
3440 * GetMenuString16 (USER.161)
3442 INT16 WINAPI GetMenuString16( HMENU16 hMenu, UINT16 wItemID,
3443 LPSTR str, INT16 nMaxSiz, UINT16 wFlags )
3445 return GetMenuStringA( hMenu, wItemID, str, nMaxSiz, wFlags );
3449 /*******************************************************************
3450 * GetMenuStringA (USER32.@)
3452 INT WINAPI GetMenuStringA(
3453 HMENU hMenu, /* [in] menuhandle */
3454 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3455 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3456 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3457 UINT wFlags /* [in] MF_ flags */
3459 MENUITEM *item;
3461 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3462 hMenu, wItemID, str, nMaxSiz, wFlags );
3463 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3464 if (!IS_STRING_ITEM(item->fType)) return 0;
3465 if (!str || !nMaxSiz) return strlenW(item->text);
3466 str[0] = '\0';
3467 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3468 str[nMaxSiz-1] = 0;
3469 TRACE("returning '%s'\n", str );
3470 return strlen(str);
3474 /*******************************************************************
3475 * GetMenuStringW (USER32.@)
3477 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3478 LPWSTR str, INT nMaxSiz, UINT wFlags )
3480 MENUITEM *item;
3482 TRACE("menu=%04x item=%04x ptr=%p len=%d flags=%04x\n",
3483 hMenu, wItemID, str, nMaxSiz, wFlags );
3484 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3485 if (!IS_STRING_ITEM(item->fType)) return 0;
3486 if (!str || !nMaxSiz) return strlenW(item->text);
3487 str[0] = '\0';
3488 lstrcpynW( str, item->text, nMaxSiz );
3489 return strlenW(str);
3493 /**********************************************************************
3494 * HiliteMenuItem16 (USER.162)
3496 BOOL16 WINAPI HiliteMenuItem16( HWND16 hWnd, HMENU16 hMenu, UINT16 wItemID,
3497 UINT16 wHilite )
3499 return HiliteMenuItem( hWnd, hMenu, wItemID, wHilite );
3503 /**********************************************************************
3504 * HiliteMenuItem (USER32.@)
3506 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3507 UINT wHilite )
3509 LPPOPUPMENU menu;
3510 TRACE("(%04x, %04x, %04x, %04x);\n",
3511 hWnd, hMenu, wItemID, wHilite);
3512 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3513 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3514 if (menu->FocusedItem == wItemID) return TRUE;
3515 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3516 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3517 return TRUE;
3521 /**********************************************************************
3522 * GetMenuState16 (USER.250)
3524 UINT16 WINAPI GetMenuState16( HMENU16 hMenu, UINT16 wItemID, UINT16 wFlags )
3526 return GetMenuState( hMenu, wItemID, wFlags );
3530 /**********************************************************************
3531 * GetMenuState (USER32.@)
3533 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3535 MENUITEM *item;
3536 TRACE("(menu=%04x, id=%04x, flags=%04x);\n",
3537 hMenu, wItemID, wFlags);
3538 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3539 debug_print_menuitem (" item: ", item, "");
3540 if (item->fType & MF_POPUP)
3542 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3543 if (!menu) return -1;
3544 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3546 else
3548 /* We used to (from way back then) mask the result to 0xff. */
3549 /* I don't know why and it seems wrong as the documented */
3550 /* return flag MF_SEPARATOR is outside that mask. */
3551 return (item->fType | item->fState);
3556 /**********************************************************************
3557 * GetMenuItemCount16 (USER.263)
3559 INT16 WINAPI GetMenuItemCount16( HMENU16 hMenu )
3561 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3562 if (!menu) return -1;
3563 TRACE("(%04x) returning %d\n",
3564 hMenu, menu->nItems );
3565 return menu->nItems;
3569 /**********************************************************************
3570 * GetMenuItemCount (USER32.@)
3572 INT WINAPI GetMenuItemCount( HMENU hMenu )
3574 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3575 if (!menu) return -1;
3576 TRACE("(%04x) returning %d\n",
3577 hMenu, menu->nItems );
3578 return menu->nItems;
3581 /**********************************************************************
3582 * GetMenuItemID16 (USER.264)
3584 UINT16 WINAPI GetMenuItemID16( HMENU16 hMenu, INT16 nPos )
3586 return (UINT16) GetMenuItemID (hMenu, nPos);
3589 /**********************************************************************
3590 * GetMenuItemID (USER32.@)
3592 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3594 MENUITEM * lpmi;
3596 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3597 if (lpmi->fType & MF_POPUP) return -1;
3598 return lpmi->wID;
3602 /*******************************************************************
3603 * InsertMenu16 (USER.410)
3605 BOOL16 WINAPI InsertMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3606 UINT16 id, SEGPTR data )
3608 UINT pos32 = (UINT)pos;
3609 if ((pos == (UINT16)-1) && (flags & MF_BYPOSITION)) pos32 = (UINT)-1;
3610 if (IS_STRING_ITEM(flags) && data)
3611 return InsertMenuA( hMenu, pos32, flags, id, MapSL(data) );
3612 return InsertMenuA( hMenu, pos32, flags, id, (LPSTR)data );
3616 /*******************************************************************
3617 * InsertMenuW (USER32.@)
3619 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3620 UINT id, LPCWSTR str )
3622 MENUITEM *item;
3624 if (IS_STRING_ITEM(flags) && str)
3625 TRACE("hMenu %04x, pos %d, flags %08x, "
3626 "id %04x, str '%s'\n",
3627 hMenu, pos, flags, id, debugstr_w(str) );
3628 else TRACE("hMenu %04x, pos %d, flags %08x, "
3629 "id %04x, str %08lx (not a string)\n",
3630 hMenu, pos, flags, id, (DWORD)str );
3632 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3634 if (!(MENU_SetItemData( item, flags, id, str )))
3636 RemoveMenu( hMenu, pos, flags );
3637 return FALSE;
3640 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3641 (MENU_GetMenu((HMENU16)id))->wFlags |= MF_POPUP;
3643 item->hCheckBit = item->hUnCheckBit = 0;
3644 return TRUE;
3648 /*******************************************************************
3649 * InsertMenuA (USER32.@)
3651 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3652 UINT id, LPCSTR str )
3654 BOOL ret;
3656 if (IS_STRING_ITEM(flags) && str)
3658 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3659 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3660 HeapFree( GetProcessHeap(), 0, newstr );
3661 return ret;
3663 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3667 /*******************************************************************
3668 * AppendMenu16 (USER.411)
3670 BOOL16 WINAPI AppendMenu16(HMENU16 hMenu, UINT16 flags, UINT16 id, SEGPTR data)
3672 return InsertMenu16( hMenu, -1, flags | MF_BYPOSITION, id, data );
3676 /*******************************************************************
3677 * AppendMenuA (USER32.@)
3679 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3680 UINT id, LPCSTR data )
3682 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3686 /*******************************************************************
3687 * AppendMenuW (USER32.@)
3689 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3690 UINT id, LPCWSTR data )
3692 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3696 /**********************************************************************
3697 * RemoveMenu16 (USER.412)
3699 BOOL16 WINAPI RemoveMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3701 return RemoveMenu( hMenu, nPos, wFlags );
3705 /**********************************************************************
3706 * RemoveMenu (USER32.@)
3708 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3710 LPPOPUPMENU menu;
3711 MENUITEM *item;
3713 TRACE("(menu=%04x pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3714 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3715 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3717 /* Remove item */
3719 MENU_FreeItemData( item );
3721 if (--menu->nItems == 0)
3723 HeapFree( SystemHeap, 0, menu->items );
3724 menu->items = NULL;
3726 else
3728 while(nPos < menu->nItems)
3730 *item = *(item+1);
3731 item++;
3732 nPos++;
3734 menu->items = HeapReAlloc( SystemHeap, 0, menu->items,
3735 menu->nItems * sizeof(MENUITEM) );
3737 return TRUE;
3741 /**********************************************************************
3742 * DeleteMenu16 (USER.413)
3744 BOOL16 WINAPI DeleteMenu16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags )
3746 return DeleteMenu( hMenu, nPos, wFlags );
3750 /**********************************************************************
3751 * DeleteMenu (USER32.@)
3753 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3755 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3756 if (!item) return FALSE;
3757 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3758 /* nPos is now the position of the item */
3759 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3760 return TRUE;
3764 /*******************************************************************
3765 * ModifyMenu16 (USER.414)
3767 BOOL16 WINAPI ModifyMenu16( HMENU16 hMenu, UINT16 pos, UINT16 flags,
3768 UINT16 id, SEGPTR data )
3770 if (IS_STRING_ITEM(flags))
3771 return ModifyMenuA( hMenu, pos, flags, id, MapSL(data) );
3772 return ModifyMenuA( hMenu, pos, flags, id, (LPSTR)data );
3776 /*******************************************************************
3777 * ModifyMenuW (USER32.@)
3779 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3780 UINT id, LPCWSTR str )
3782 MENUITEM *item;
3784 if (IS_STRING_ITEM(flags))
3786 TRACE("%04x %d %04x %04x '%s'\n",
3787 hMenu, pos, flags, id, str ? debugstr_w(str) : "#NULL#" );
3788 if (!str) return FALSE;
3790 else
3792 TRACE("%04x %d %04x %04x %08lx\n",
3793 hMenu, pos, flags, id, (DWORD)str );
3796 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3797 return MENU_SetItemData( item, flags, id, str );
3801 /*******************************************************************
3802 * ModifyMenuA (USER32.@)
3804 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3805 UINT id, LPCSTR str )
3807 BOOL ret;
3809 if (IS_STRING_ITEM(flags) && str)
3811 LPWSTR newstr = HEAP_strdupAtoW( GetProcessHeap(), 0, str );
3812 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3813 HeapFree( GetProcessHeap(), 0, newstr );
3814 return ret;
3816 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3820 /**********************************************************************
3821 * CreatePopupMenu16 (USER.415)
3823 HMENU16 WINAPI CreatePopupMenu16(void)
3825 return CreatePopupMenu();
3829 /**********************************************************************
3830 * CreatePopupMenu (USER32.@)
3832 HMENU WINAPI CreatePopupMenu(void)
3834 HMENU hmenu;
3835 POPUPMENU *menu;
3837 if (!(hmenu = CreateMenu())) return 0;
3838 menu = (POPUPMENU *) USER_HEAP_LIN_ADDR( hmenu );
3839 menu->wFlags |= MF_POPUP;
3840 menu->bTimeToHide = FALSE;
3841 return hmenu;
3845 /**********************************************************************
3846 * GetMenuCheckMarkDimensions (USER.417) (USER32.@)
3848 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3850 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3854 /**********************************************************************
3855 * SetMenuItemBitmaps16 (USER.418)
3857 BOOL16 WINAPI SetMenuItemBitmaps16( HMENU16 hMenu, UINT16 nPos, UINT16 wFlags,
3858 HBITMAP16 hNewUnCheck, HBITMAP16 hNewCheck)
3860 return SetMenuItemBitmaps( hMenu, nPos, wFlags, hNewUnCheck, hNewCheck );
3864 /**********************************************************************
3865 * SetMenuItemBitmaps (USER32.@)
3867 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3868 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3870 MENUITEM *item;
3871 TRACE("(%04x, %04x, %04x, %04x, %04x)\n",
3872 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3873 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3875 if (!hNewCheck && !hNewUnCheck)
3877 item->fState &= ~MF_USECHECKBITMAPS;
3879 else /* Install new bitmaps */
3881 item->hCheckBit = hNewCheck;
3882 item->hUnCheckBit = hNewUnCheck;
3883 item->fState |= MF_USECHECKBITMAPS;
3885 return TRUE;
3889 /**********************************************************************
3890 * CreateMenu16 (USER.151)
3892 HMENU16 WINAPI CreateMenu16(void)
3894 return CreateMenu();
3898 /**********************************************************************
3899 * CreateMenu (USER32.@)
3901 HMENU WINAPI CreateMenu(void)
3903 HMENU hMenu;
3904 LPPOPUPMENU menu;
3905 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3906 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3908 ZeroMemory(menu, sizeof(POPUPMENU));
3909 menu->wMagic = MENU_MAGIC;
3910 menu->FocusedItem = NO_SELECTED_ITEM;
3911 menu->bTimeToHide = FALSE;
3913 TRACE("return %04x\n", hMenu );
3915 return hMenu;
3919 /**********************************************************************
3920 * DestroyMenu16 (USER.152)
3922 BOOL16 WINAPI DestroyMenu16( HMENU16 hMenu )
3924 return DestroyMenu( hMenu );
3928 /**********************************************************************
3929 * DestroyMenu (USER32.@)
3931 BOOL WINAPI DestroyMenu( HMENU hMenu )
3933 TRACE("(%04x)\n", hMenu);
3935 /* Silently ignore attempts to destroy default system popup */
3937 if (hMenu && hMenu != MENU_DefSysPopup)
3939 LPPOPUPMENU lppop = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3940 WND *pTPWnd = MENU_GetTopPopupWnd();
3942 if( pTPWnd && (hMenu == *(HMENU*)pTPWnd->wExtra) )
3943 *(UINT*)pTPWnd->wExtra = 0;
3945 if (!IS_A_MENU(lppop)) lppop = NULL;
3946 if ( lppop )
3948 lppop->wMagic = 0; /* Mark it as destroyed */
3950 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd &&
3951 (!pTPWnd || (lppop->hWnd != pTPWnd->hwndSelf)))
3952 DestroyWindow( lppop->hWnd );
3954 if (lppop->items) /* recursively destroy submenus */
3956 int i;
3957 MENUITEM *item = lppop->items;
3958 for (i = lppop->nItems; i > 0; i--, item++)
3960 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3961 MENU_FreeItemData( item );
3963 HeapFree( SystemHeap, 0, lppop->items );
3965 USER_HEAP_FREE( hMenu );
3966 MENU_ReleaseTopPopupWnd();
3968 else
3970 MENU_ReleaseTopPopupWnd();
3971 return FALSE;
3974 return (hMenu != MENU_DefSysPopup);
3978 /**********************************************************************
3979 * GetSystemMenu16 (USER.156)
3981 HMENU16 WINAPI GetSystemMenu16( HWND16 hWnd, BOOL16 bRevert )
3983 return GetSystemMenu( hWnd, bRevert );
3987 /**********************************************************************
3988 * GetSystemMenu (USER32.@)
3990 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3992 WND *wndPtr = WIN_FindWndPtr( hWnd );
3993 HMENU retvalue = 0;
3995 if (wndPtr)
3997 if( wndPtr->hSysMenu )
3999 if( bRevert )
4001 DestroyMenu(wndPtr->hSysMenu);
4002 wndPtr->hSysMenu = 0;
4004 else
4006 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
4007 if( menu )
4009 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
4010 menu->items[0].hSubMenu = MENU_CopySysPopup();
4012 else
4014 WARN("Current sys-menu (%04x) of wnd %04x is broken\n",
4015 wndPtr->hSysMenu, hWnd);
4016 wndPtr->hSysMenu = 0;
4021 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4022 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
4024 if( wndPtr->hSysMenu )
4026 POPUPMENU *menu;
4027 retvalue = GetSubMenu16(wndPtr->hSysMenu, 0);
4029 /* Store the dummy sysmenu handle to facilitate the refresh */
4030 /* of the close button if the SC_CLOSE item change */
4031 menu = MENU_GetMenu(retvalue);
4032 if ( menu )
4033 menu->hSysMenuOwner = wndPtr->hSysMenu;
4035 WIN_ReleaseWndPtr(wndPtr);
4037 return bRevert ? 0 : retvalue;
4041 /*******************************************************************
4042 * SetSystemMenu16 (USER.280)
4044 BOOL16 WINAPI SetSystemMenu16( HWND16 hwnd, HMENU16 hMenu )
4046 return SetSystemMenu( hwnd, hMenu );
4050 /*******************************************************************
4051 * SetSystemMenu (USER32.@)
4053 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4055 WND *wndPtr = WIN_FindWndPtr(hwnd);
4057 if (wndPtr)
4059 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4060 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4061 WIN_ReleaseWndPtr(wndPtr);
4062 return TRUE;
4064 return FALSE;
4068 /**********************************************************************
4069 * GetMenu16 (USER.157)
4071 HMENU16 WINAPI GetMenu16( HWND16 hWnd )
4073 return (HMENU16)GetMenu(hWnd);
4077 /**********************************************************************
4078 * GetMenu (USER32.@)
4080 HMENU WINAPI GetMenu( HWND hWnd )
4082 HMENU retvalue;
4083 WND * wndPtr = WIN_FindWndPtr(hWnd);
4085 if (!wndPtr) return 0;
4087 retvalue = (HMENU)wndPtr->wIDmenu;
4088 TRACE("for %swindow %04x returning %04x\n",
4089 (wndPtr->dwStyle & WS_CHILD) ? "child " : "", hWnd, retvalue);
4090 WIN_ReleaseWndPtr(wndPtr);
4091 return retvalue;
4095 /**********************************************************************
4096 * SetMenu16 (USER.158)
4098 BOOL16 WINAPI SetMenu16( HWND16 hWnd, HMENU16 hMenu )
4100 return SetMenu( hWnd, hMenu );
4104 /**********************************************************************
4105 * SetMenu (USER32.@)
4107 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4109 WND * wndPtr = WIN_FindWndPtr(hWnd);
4110 BOOL res = FALSE;
4112 TRACE("(%04x, %04x);\n", hWnd, hMenu);
4114 if (hMenu && !IsMenu(hMenu))
4116 WARN("hMenu is not a menu handle\n");
4117 goto exit;
4120 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD))
4122 if (GetCapture() == hWnd) ReleaseCapture();
4124 wndPtr->wIDmenu = (UINT)hMenu;
4125 if (hMenu != 0)
4127 LPPOPUPMENU lpmenu;
4129 if (!(lpmenu = MENU_GetMenu(hMenu)))
4130 goto exit;
4132 lpmenu->hWnd = hWnd;
4133 lpmenu->Height = 0; /* Make sure we recalculate the size */
4135 if (IsWindowVisible(hWnd))
4136 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4137 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4138 res = TRUE;
4140 exit:
4141 WIN_ReleaseWndPtr(wndPtr);
4142 return res;
4147 /**********************************************************************
4148 * GetSubMenu16 (USER.159)
4150 HMENU16 WINAPI GetSubMenu16( HMENU16 hMenu, INT16 nPos )
4152 return GetSubMenu( hMenu, nPos );
4156 /**********************************************************************
4157 * GetSubMenu (USER32.@)
4159 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4161 MENUITEM * lpmi;
4163 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
4164 if (!(lpmi->fType & MF_POPUP)) return 0;
4165 return lpmi->hSubMenu;
4169 /**********************************************************************
4170 * DrawMenuBar16 (USER.160)
4172 void WINAPI DrawMenuBar16( HWND16 hWnd )
4174 DrawMenuBar( hWnd );
4178 /**********************************************************************
4179 * DrawMenuBar (USER32.@)
4181 BOOL WINAPI DrawMenuBar( HWND hWnd )
4183 LPPOPUPMENU lppop;
4184 WND *wndPtr = WIN_FindWndPtr(hWnd);
4185 if (wndPtr && !(wndPtr->dwStyle & WS_CHILD) && wndPtr->wIDmenu)
4187 lppop = MENU_GetMenu((HMENU16)wndPtr->wIDmenu);
4188 if (lppop == NULL)
4190 WIN_ReleaseWndPtr(wndPtr);
4191 return FALSE;
4194 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4195 lppop->hwndOwner = hWnd;
4196 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4197 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4198 WIN_ReleaseWndPtr(wndPtr);
4199 return TRUE;
4201 WIN_ReleaseWndPtr(wndPtr);
4202 return FALSE;
4206 /***********************************************************************
4207 * EndMenu (USER.187) (USER32.@)
4209 void WINAPI EndMenu(void)
4211 /* if we are in the menu code, and it is active */
4212 if (fEndMenu == FALSE && MENU_IsMenuActive())
4214 /* terminate the menu handling code */
4215 fEndMenu = TRUE;
4217 /* needs to be posted to wakeup the internal menu handler */
4218 /* which will now terminate the menu, in the event that */
4219 /* the main window was minimized, or lost focus, so we */
4220 /* don't end up with an orphaned menu */
4221 PostMessageA( pTopPopupWnd->hwndSelf, WM_CANCELMODE, 0, 0);
4226 /***********************************************************************
4227 * LookupMenuHandle (USER.217)
4229 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4231 HMENU hmenu32 = hmenu;
4232 UINT id32 = id;
4233 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4234 else return hmenu32;
4238 /**********************************************************************
4239 * LoadMenu16 (USER.150)
4241 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4243 HRSRC16 hRsrc;
4244 HGLOBAL16 handle;
4245 HMENU16 hMenu;
4247 TRACE("(%04x,%s)\n", instance, debugres_a(name) );
4249 if (HIWORD(name))
4251 if (name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4254 if (!name) return 0;
4256 /* check for Win32 module */
4257 if (HIWORD(instance)) return LoadMenuA( instance, name );
4258 instance = GetExePtr( instance );
4260 if (!(hRsrc = FindResource16( instance, name, RT_MENUA ))) return 0;
4261 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4262 hMenu = LoadMenuIndirect16(LockResource16(handle));
4263 FreeResource16( handle );
4264 return hMenu;
4268 /*****************************************************************
4269 * LoadMenuA (USER32.@)
4271 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4273 HRSRC hrsrc = FindResourceA( instance, name, RT_MENUA );
4274 if (!hrsrc) return 0;
4275 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4279 /*****************************************************************
4280 * LoadMenuW (USER32.@)
4282 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4284 HRSRC hrsrc = FindResourceW( instance, name, RT_MENUW );
4285 if (!hrsrc) return 0;
4286 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4290 /**********************************************************************
4291 * LoadMenuIndirect16 (USER.220)
4293 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4295 HMENU16 hMenu;
4296 WORD version, offset;
4297 LPCSTR p = (LPCSTR)template;
4299 TRACE("(%p)\n", template );
4300 version = GET_WORD(p);
4301 p += sizeof(WORD);
4302 if (version)
4304 WARN("version must be 0 for Win16\n" );
4305 return 0;
4307 offset = GET_WORD(p);
4308 p += sizeof(WORD) + offset;
4309 if (!(hMenu = CreateMenu())) return 0;
4310 if (!MENU_ParseResource( p, hMenu, FALSE ))
4312 DestroyMenu( hMenu );
4313 return 0;
4315 return hMenu;
4319 /**********************************************************************
4320 * LoadMenuIndirectA (USER32.@)
4322 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4324 HMENU16 hMenu;
4325 WORD version, offset;
4326 LPCSTR p = (LPCSTR)template;
4328 TRACE("%p\n", template );
4329 version = GET_WORD(p);
4330 p += sizeof(WORD);
4331 switch (version)
4333 case 0:
4334 offset = GET_WORD(p);
4335 p += sizeof(WORD) + offset;
4336 if (!(hMenu = CreateMenu())) return 0;
4337 if (!MENU_ParseResource( p, hMenu, TRUE ))
4339 DestroyMenu( hMenu );
4340 return 0;
4342 return hMenu;
4343 case 1:
4344 offset = GET_WORD(p);
4345 p += sizeof(WORD) + offset;
4346 if (!(hMenu = CreateMenu())) return 0;
4347 if (!MENUEX_ParseResource( p, hMenu))
4349 DestroyMenu( hMenu );
4350 return 0;
4352 return hMenu;
4353 default:
4354 ERR("version %d not supported.\n", version);
4355 return 0;
4360 /**********************************************************************
4361 * LoadMenuIndirectW (USER32.@)
4363 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4365 /* FIXME: is there anything different between A and W? */
4366 return LoadMenuIndirectA( template );
4370 /**********************************************************************
4371 * IsMenu16 (USER.358)
4373 BOOL16 WINAPI IsMenu16( HMENU16 hmenu )
4375 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4376 return IS_A_MENU(menu);
4380 /**********************************************************************
4381 * IsMenu (USER32.@)
4383 BOOL WINAPI IsMenu(HMENU hmenu)
4385 LPPOPUPMENU menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hmenu);
4386 return IS_A_MENU(menu);
4389 /**********************************************************************
4390 * GetMenuItemInfo_common
4393 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4394 LPMENUITEMINFOW lpmii, BOOL unicode)
4396 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4398 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4400 if (!menu)
4401 return FALSE;
4403 if (lpmii->fMask & MIIM_TYPE) {
4404 lpmii->fType = menu->fType;
4405 switch (MENU_ITEM_TYPE(menu->fType)) {
4406 case MF_STRING:
4407 break; /* will be done below */
4408 case MF_OWNERDRAW:
4409 case MF_BITMAP:
4410 lpmii->dwTypeData = menu->text;
4411 /* fall through */
4412 default:
4413 lpmii->cch = 0;
4417 /* copy the text string */
4418 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4419 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4421 int len;
4422 if (unicode)
4424 len = strlenW(menu->text);
4425 if(lpmii->dwTypeData && lpmii->cch)
4426 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4428 else
4430 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4431 if(lpmii->dwTypeData && lpmii->cch)
4432 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4433 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4434 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4436 /* if we've copied a substring we return its length */
4437 if(lpmii->dwTypeData && lpmii->cch)
4439 if (lpmii->cch <= len) lpmii->cch--;
4441 else /* return length of string */
4442 lpmii->cch = len;
4445 if (lpmii->fMask & MIIM_FTYPE)
4446 lpmii->fType = menu->fType;
4448 if (lpmii->fMask & MIIM_BITMAP)
4449 lpmii->hbmpItem = menu->hbmpItem;
4451 if (lpmii->fMask & MIIM_STATE)
4452 lpmii->fState = menu->fState;
4454 if (lpmii->fMask & MIIM_ID)
4455 lpmii->wID = menu->wID;
4457 if (lpmii->fMask & MIIM_SUBMENU)
4458 lpmii->hSubMenu = menu->hSubMenu;
4460 if (lpmii->fMask & MIIM_CHECKMARKS) {
4461 lpmii->hbmpChecked = menu->hCheckBit;
4462 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4464 if (lpmii->fMask & MIIM_DATA)
4465 lpmii->dwItemData = menu->dwItemData;
4467 return TRUE;
4470 /**********************************************************************
4471 * GetMenuItemInfoA (USER32.@)
4473 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4474 LPMENUITEMINFOA lpmii)
4476 return GetMenuItemInfo_common (hmenu, item, bypos,
4477 (LPMENUITEMINFOW)lpmii, FALSE);
4480 /**********************************************************************
4481 * GetMenuItemInfoW (USER32.@)
4483 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4484 LPMENUITEMINFOW lpmii)
4486 return GetMenuItemInfo_common (hmenu, item, bypos,
4487 lpmii, TRUE);
4490 /**********************************************************************
4491 * SetMenuItemInfo_common
4494 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4495 const MENUITEMINFOW *lpmii,
4496 BOOL unicode)
4498 if (!menu) return FALSE;
4500 if (lpmii->fMask & MIIM_TYPE ) {
4501 /* Get rid of old string. */
4502 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4503 HeapFree(SystemHeap, 0, menu->text);
4504 menu->text = NULL;
4507 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4508 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4509 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4511 menu->text = lpmii->dwTypeData;
4513 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4514 if (unicode)
4515 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4516 else
4517 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR)lpmii->dwTypeData);
4521 if (lpmii->fMask & MIIM_FTYPE ) {
4522 /* free the string when the type is changing */
4523 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4524 HeapFree(SystemHeap, 0, menu->text);
4525 menu->text = NULL;
4527 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4528 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4531 if (lpmii->fMask & MIIM_STRING ) {
4532 /* free the string when used */
4533 if ( IS_STRING_ITEM(menu->fType) && menu->text) {
4534 HeapFree(SystemHeap, 0, menu->text);
4535 if (unicode)
4536 menu->text = HEAP_strdupW(SystemHeap, 0, lpmii->dwTypeData);
4537 else
4538 menu->text = HEAP_strdupAtoW(SystemHeap, 0, (LPSTR) lpmii->dwTypeData);
4542 if (lpmii->fMask & MIIM_STATE)
4544 /* fixme: MFS_DEFAULT do we have to reset the other menu items? */
4545 menu->fState = lpmii->fState;
4548 if (lpmii->fMask & MIIM_ID)
4549 menu->wID = lpmii->wID;
4551 if (lpmii->fMask & MIIM_SUBMENU) {
4552 menu->hSubMenu = lpmii->hSubMenu;
4553 if (menu->hSubMenu) {
4554 POPUPMENU *subMenu = MENU_GetMenu((UINT16)menu->hSubMenu);
4555 if (subMenu) {
4556 subMenu->wFlags |= MF_POPUP;
4557 menu->fType |= MF_POPUP;
4559 else
4560 /* FIXME: Return an error ? */
4561 menu->fType &= ~MF_POPUP;
4563 else
4564 menu->fType &= ~MF_POPUP;
4567 if (lpmii->fMask & MIIM_CHECKMARKS)
4569 if (lpmii->fType & MFT_RADIOCHECK)
4570 menu->fType |= MFT_RADIOCHECK;
4572 menu->hCheckBit = lpmii->hbmpChecked;
4573 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4575 if (lpmii->fMask & MIIM_DATA)
4576 menu->dwItemData = lpmii->dwItemData;
4578 debug_print_menuitem("SetMenuItemInfo_common: ", menu, "");
4579 return TRUE;
4582 /**********************************************************************
4583 * SetMenuItemInfoA (USER32.@)
4585 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4586 const MENUITEMINFOA *lpmii)
4588 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4589 (const MENUITEMINFOW *)lpmii, FALSE);
4592 /**********************************************************************
4593 * SetMenuItemInfoW (USER32.@)
4595 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4596 const MENUITEMINFOW *lpmii)
4598 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4599 lpmii, TRUE);
4602 /**********************************************************************
4603 * SetMenuDefaultItem (USER32.@)
4606 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4608 UINT i;
4609 POPUPMENU *menu;
4610 MENUITEM *item;
4612 TRACE("(0x%x,%d,%d)\n", hmenu, uItem, bypos);
4614 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4616 /* reset all default-item flags */
4617 item = menu->items;
4618 for (i = 0; i < menu->nItems; i++, item++)
4620 item->fState &= ~MFS_DEFAULT;
4623 /* no default item */
4624 if ( -1 == uItem)
4626 return TRUE;
4629 item = menu->items;
4630 if ( bypos )
4632 if ( uItem >= menu->nItems ) return FALSE;
4633 item[uItem].fState |= MFS_DEFAULT;
4634 return TRUE;
4636 else
4638 for (i = 0; i < menu->nItems; i++, item++)
4640 if (item->wID == uItem)
4642 item->fState |= MFS_DEFAULT;
4643 return TRUE;
4648 return FALSE;
4651 /**********************************************************************
4652 * GetMenuDefaultItem (USER32.@)
4654 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4656 POPUPMENU *menu;
4657 MENUITEM * item;
4658 UINT i = 0;
4660 TRACE("(0x%x,%d,%d)\n", hmenu, bypos, flags);
4662 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4664 /* find default item */
4665 item = menu->items;
4667 /* empty menu */
4668 if (! item) return -1;
4670 while ( !( item->fState & MFS_DEFAULT ) )
4672 i++; item++;
4673 if (i >= menu->nItems ) return -1;
4676 /* default: don't return disabled items */
4677 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4679 /* search rekursiv when needed */
4680 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4682 UINT ret;
4683 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4684 if ( -1 != ret ) return ret;
4686 /* when item not found in submenu, return the popup item */
4688 return ( bypos ) ? i : item->wID;
4692 /*******************************************************************
4693 * InsertMenuItem16 (USER.441)
4695 * FIXME: untested
4697 BOOL16 WINAPI InsertMenuItem16( HMENU16 hmenu, UINT16 pos, BOOL16 byposition,
4698 const MENUITEMINFO16 *mii )
4700 MENUITEMINFOA miia;
4702 miia.cbSize = sizeof(miia);
4703 miia.fMask = mii->fMask;
4704 miia.dwTypeData = (LPSTR)mii->dwTypeData;
4705 miia.fType = mii->fType;
4706 miia.fState = mii->fState;
4707 miia.wID = mii->wID;
4708 miia.hSubMenu = mii->hSubMenu;
4709 miia.hbmpChecked = mii->hbmpChecked;
4710 miia.hbmpUnchecked = mii->hbmpUnchecked;
4711 miia.dwItemData = mii->dwItemData;
4712 miia.cch = mii->cch;
4713 if (IS_STRING_ITEM(miia.fType))
4714 miia.dwTypeData = MapSL(mii->dwTypeData);
4715 return InsertMenuItemA( hmenu, pos, byposition, &miia );
4719 /**********************************************************************
4720 * InsertMenuItemA (USER32.@)
4722 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4723 const MENUITEMINFOA *lpmii)
4725 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4726 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4730 /**********************************************************************
4731 * InsertMenuItemW (USER32.@)
4733 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4734 const MENUITEMINFOW *lpmii)
4736 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4737 return SetMenuItemInfo_common(item, lpmii, TRUE);
4740 /**********************************************************************
4741 * CheckMenuRadioItem (USER32.@)
4744 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4745 UINT first, UINT last, UINT check,
4746 UINT bypos)
4748 MENUITEM *mifirst, *milast, *micheck;
4749 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4751 TRACE("ox%x: %d-%d, check %d, bypos=%d\n",
4752 hMenu, first, last, check, bypos);
4754 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4755 milast = MENU_FindItem (&mlast, &last, bypos);
4756 micheck = MENU_FindItem (&mcheck, &check, bypos);
4758 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4759 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4760 micheck > milast || micheck < mifirst)
4761 return FALSE;
4763 while (mifirst <= milast)
4765 if (mifirst == micheck)
4767 mifirst->fType |= MFT_RADIOCHECK;
4768 mifirst->fState |= MFS_CHECKED;
4769 } else {
4770 mifirst->fType &= ~MFT_RADIOCHECK;
4771 mifirst->fState &= ~MFS_CHECKED;
4773 mifirst++;
4776 return TRUE;
4779 /**********************************************************************
4780 * CheckMenuRadioItem16 (not a Windows API)
4783 BOOL16 WINAPI CheckMenuRadioItem16(HMENU16 hMenu,
4784 UINT16 first, UINT16 last, UINT16 check,
4785 BOOL16 bypos)
4787 return CheckMenuRadioItem (hMenu, first, last, check, bypos);
4790 /**********************************************************************
4791 * GetMenuItemRect (USER32.@)
4793 * ATTENTION: Here, the returned values in rect are the screen
4794 * coordinates of the item just like if the menu was
4795 * always on the upper left side of the application.
4798 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4799 LPRECT rect)
4801 POPUPMENU *itemMenu;
4802 MENUITEM *item;
4803 HWND referenceHwnd;
4805 TRACE("(0x%x,0x%x,%d,%p)\n", hwnd, hMenu, uItem, rect);
4807 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4808 referenceHwnd = hwnd;
4810 if(!hwnd)
4812 itemMenu = MENU_GetMenu(hMenu);
4813 if (itemMenu == NULL)
4814 return FALSE;
4816 if(itemMenu->hWnd == 0)
4817 return FALSE;
4818 referenceHwnd = itemMenu->hWnd;
4821 if ((rect == NULL) || (item == NULL))
4822 return FALSE;
4824 *rect = item->rect;
4826 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4828 return TRUE;
4831 /**********************************************************************
4832 * GetMenuItemRect16 (USER.665)
4835 BOOL16 WINAPI GetMenuItemRect16 (HWND16 hwnd, HMENU16 hMenu, UINT16 uItem,
4836 LPRECT16 rect)
4838 RECT r32;
4839 BOOL res;
4841 if (!rect) return FALSE;
4842 res = GetMenuItemRect (hwnd, hMenu, uItem, &r32);
4843 CONV_RECT32TO16 (&r32, rect);
4844 return res;
4847 /**********************************************************************
4848 * SetMenuInfo (USER32.@)
4850 * FIXME
4851 * MIM_APPLYTOSUBMENUS
4852 * actually use the items to draw the menu
4854 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4856 POPUPMENU *menu;
4858 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4860 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4863 if (lpmi->fMask & MIM_BACKGROUND)
4864 menu->hbrBack = lpmi->hbrBack;
4866 if (lpmi->fMask & MIM_HELPID)
4867 menu->dwContextHelpID = lpmi->dwContextHelpID;
4869 if (lpmi->fMask & MIM_MAXHEIGHT)
4870 menu->cyMax = lpmi->cyMax;
4872 if (lpmi->fMask & MIM_MENUDATA)
4873 menu->dwMenuData = lpmi->dwMenuData;
4875 if (lpmi->fMask & MIM_STYLE)
4876 menu->dwStyle = lpmi->dwStyle;
4878 return TRUE;
4880 return FALSE;
4883 /**********************************************************************
4884 * GetMenuInfo (USER32.@)
4886 * NOTES
4887 * win98/NT5.0
4890 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4891 { POPUPMENU *menu;
4893 TRACE("(0x%04x %p)\n", hMenu, lpmi);
4895 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4898 if (lpmi->fMask & MIM_BACKGROUND)
4899 lpmi->hbrBack = menu->hbrBack;
4901 if (lpmi->fMask & MIM_HELPID)
4902 lpmi->dwContextHelpID = menu->dwContextHelpID;
4904 if (lpmi->fMask & MIM_MAXHEIGHT)
4905 lpmi->cyMax = menu->cyMax;
4907 if (lpmi->fMask & MIM_MENUDATA)
4908 lpmi->dwMenuData = menu->dwMenuData;
4910 if (lpmi->fMask & MIM_STYLE)
4911 lpmi->dwStyle = menu->dwStyle;
4913 return TRUE;
4915 return FALSE;
4918 /**********************************************************************
4919 * SetMenuContextHelpId16 (USER.384)
4921 BOOL16 WINAPI SetMenuContextHelpId16( HMENU16 hMenu, DWORD dwContextHelpID)
4923 return SetMenuContextHelpId( hMenu, dwContextHelpID );
4927 /**********************************************************************
4928 * SetMenuContextHelpId (USER32.@)
4930 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4932 LPPOPUPMENU menu;
4934 TRACE("(0x%04x 0x%08lx)\n", hMenu, dwContextHelpID);
4936 if ((menu = MENU_GetMenu(hMenu)))
4938 menu->dwContextHelpID = dwContextHelpID;
4939 return TRUE;
4941 return FALSE;
4944 /**********************************************************************
4945 * GetMenuContextHelpId16 (USER.385)
4947 DWORD WINAPI GetMenuContextHelpId16( HMENU16 hMenu )
4949 return GetMenuContextHelpId( hMenu );
4952 /**********************************************************************
4953 * GetMenuContextHelpId (USER32.@)
4955 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4957 LPPOPUPMENU menu;
4959 TRACE("(0x%04x)\n", hMenu);
4961 if ((menu = MENU_GetMenu(hMenu)))
4963 return menu->dwContextHelpID;
4965 return 0;
4968 /**********************************************************************
4969 * MenuItemFromPoint (USER32.@)
4971 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4973 FIXME("(0x%04x,0x%04x,(%ld,%ld)):stub\n",
4974 hWnd, hMenu, ptScreen.x, ptScreen.y);
4975 return 0;
4979 /**********************************************************************
4980 * translate_accelerator
4982 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4983 BYTE fVirt, WORD key, WORD cmd )
4985 UINT mesg = 0;
4987 if (wParam != key) return FALSE;
4989 if (message == WM_CHAR)
4991 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4993 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4994 goto found;
4997 else
4999 if(fVirt & FVIRTKEY)
5001 INT mask = 0;
5002 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5003 wParam, 0xff & HIWORD(lParam));
5004 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5005 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5006 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5007 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5008 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5010 else
5012 if (!(lParam & 0x01000000)) /* no special_key */
5014 if ((fVirt & FALT) && (lParam & 0x20000000))
5015 { /* ^^ ALT pressed */
5016 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5017 goto found;
5022 return FALSE;
5024 found:
5025 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5026 mesg = 1;
5027 else if (GetCapture())
5028 mesg = 2;
5029 else if (!IsWindowEnabled(hWnd))
5030 mesg = 3;
5031 else
5033 HMENU hMenu, hSubMenu, hSysMenu;
5034 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5035 WND* wndPtr = WIN_FindWndPtr(hWnd);
5037 hMenu = (wndPtr->dwStyle & WS_CHILD) ? 0 : (HMENU)wndPtr->wIDmenu;
5038 hSysMenu = wndPtr->hSysMenu;
5039 WIN_ReleaseWndPtr(wndPtr);
5041 /* find menu item and ask application to initialize it */
5042 /* 1. in the system menu */
5043 hSubMenu = hSysMenu;
5044 nPos = cmd;
5045 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5047 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5048 if(hSubMenu != hSysMenu)
5050 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5051 TRACE_(accel)("hSysMenu = %04x, hSubMenu = %04x, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5052 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5054 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5056 else /* 2. in the window's menu */
5058 hSubMenu = hMenu;
5059 nPos = cmd;
5060 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5062 SendMessageA(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5063 if(hSubMenu != hMenu)
5065 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5066 TRACE_(accel)("hMenu = %04x, hSubMenu = %04x, nPos = %d\n", hMenu, hSubMenu, nPos);
5067 SendMessageA(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5069 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5073 if (uSysStat != (UINT)-1)
5075 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5076 mesg=4;
5077 else
5078 mesg=WM_SYSCOMMAND;
5080 else
5082 if (uStat != (UINT)-1)
5084 if (IsIconic(hWnd))
5085 mesg=5;
5086 else
5088 if (uStat & (MF_DISABLED|MF_GRAYED))
5089 mesg=6;
5090 else
5091 mesg=WM_COMMAND;
5094 else
5095 mesg=WM_COMMAND;
5099 if( mesg==WM_COMMAND )
5101 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5102 SendMessageA(hWnd, mesg, 0x10000 | cmd, 0L);
5104 else if( mesg==WM_SYSCOMMAND )
5106 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5107 SendMessageA(hWnd, mesg, cmd, 0x00010000L);
5109 else
5111 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5112 * #0: unknown (please report!)
5113 * #1: for WM_KEYUP,WM_SYSKEYUP
5114 * #2: mouse is captured
5115 * #3: window is disabled
5116 * #4: it's a disabled system menu option
5117 * #5: it's a menu option, but window is iconic
5118 * #6: it's a menu option, but disabled
5120 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5121 if(mesg==0)
5122 ERR_(accel)(" unknown reason - please report!");
5124 return TRUE;
5127 /**********************************************************************
5128 * TranslateAccelerator (USER32.@)
5130 INT WINAPI TranslateAccelerator( HWND hWnd, HACCEL hAccel, LPMSG msg )
5132 /* YES, Accel16! */
5133 LPACCEL16 lpAccelTbl;
5134 int i;
5136 if (msg == NULL)
5138 WARN_(accel)("msg null; should hang here to be win compatible\n");
5139 return 0;
5141 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5143 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5144 return 0;
5146 if ((msg->message != WM_KEYDOWN &&
5147 msg->message != WM_KEYUP &&
5148 msg->message != WM_SYSKEYDOWN &&
5149 msg->message != WM_SYSKEYUP &&
5150 msg->message != WM_CHAR)) return 0;
5152 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5153 "msg->hwnd=%04x, msg->message=%04x, wParam=%08x, lParam=%lx\n",
5154 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5156 i = 0;
5159 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5160 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5161 return 1;
5162 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5163 WARN_(accel)("couldn't translate accelerator key\n");
5164 return 0;
5168 /**********************************************************************
5169 * TranslateAccelerator16 (USER.178)
5171 INT16 WINAPI TranslateAccelerator16( HWND16 hWnd, HACCEL16 hAccel, LPMSG16 msg )
5173 LPACCEL16 lpAccelTbl;
5174 int i;
5176 if (msg == NULL)
5178 WARN_(accel)("msg null; should hang here to be win compatible\n");
5179 return 0;
5181 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(hAccel)))
5183 WARN_(accel)("invalid accel handle=%x\n", hAccel);
5184 return 0;
5186 if ((msg->message != WM_KEYDOWN &&
5187 msg->message != WM_KEYUP &&
5188 msg->message != WM_SYSKEYDOWN &&
5189 msg->message != WM_SYSKEYUP &&
5190 msg->message != WM_CHAR)) return 0;
5192 TRACE_(accel)("TranslateAccelerators hAccel=%04x, hWnd=%04x,"
5193 "msg->hwnd=%04x, msg->message=%04x, wParam=%04x, lParam=%lx\n",
5194 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5196 i = 0;
5199 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5200 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd ))
5201 return 1;
5202 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5203 WARN_(accel)("couldn't translate accelerator key\n");
5204 return 0;