- CoSetState info should be thread local.
[wine/hacks.git] / controls / menu.c
blobb565f087924040f228557d370598c1517bff2490
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Note: the style MF_MOUSESELECT is used to mark popup items that
25 * have been selected, i.e. their popup menu is currently displayed.
26 * This is probably not the meaning this style has in MS-Windows.
29 #include "config.h"
30 #include "wine/port.h"
32 #include <stdarg.h>
33 #include <string.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "wingdi.h"
38 #include "winnls.h"
39 #include "wine/winbase16.h"
40 #include "wine/winuser16.h"
41 #include "wownt32.h"
42 #include "wine/server.h"
43 #include "wine/unicode.h"
44 #include "win.h"
45 #include "controls.h"
46 #include "nonclient.h"
47 #include "user.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(menu);
51 WINE_DECLARE_DEBUG_CHANNEL(accel);
53 /* internal popup menu window messages */
55 #define MM_SETMENUHANDLE (WM_USER + 0)
56 #define MM_GETMENUHANDLE (WM_USER + 1)
58 /* Menu item structure */
59 typedef struct {
60 /* ----------- MENUITEMINFO Stuff ----------- */
61 UINT fType; /* Item type. */
62 UINT fState; /* Item state. */
63 UINT_PTR wID; /* Item id. */
64 HMENU hSubMenu; /* Pop-up menu. */
65 HBITMAP hCheckBit; /* Bitmap when checked. */
66 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
67 LPWSTR text; /* Item text or bitmap handle. */
68 DWORD dwItemData; /* Application defined. */
69 DWORD dwTypeData; /* depends on fMask */
70 HBITMAP hbmpItem; /* bitmap in win98 style menus */
71 /* ----------- Wine stuff ----------- */
72 RECT rect; /* Item area (relative to menu window) */
73 UINT xTab; /* X position of text after Tab */
74 } MENUITEM;
76 /* Popup menu structure */
77 typedef struct {
78 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
79 WORD wMagic; /* Magic number */
80 WORD Width; /* Width of the whole menu */
81 WORD Height; /* Height of the whole menu */
82 UINT nItems; /* Number of items in the menu */
83 HWND hWnd; /* Window containing the menu */
84 MENUITEM *items; /* Array of menu items */
85 UINT FocusedItem; /* Currently focused item */
86 HWND hwndOwner; /* window receiving the messages for ownerdraw */
87 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
88 /* ------------ MENUINFO members ------ */
89 DWORD dwStyle; /* Extended mennu style */
90 UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
91 HBRUSH hbrBack; /* brush for menu background */
92 DWORD dwContextHelpID;
93 DWORD dwMenuData; /* application defined value */
94 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
95 } POPUPMENU, *LPPOPUPMENU;
97 /* internal flags for menu tracking */
99 #define TF_ENDMENU 0x0001
100 #define TF_SUSPENDPOPUP 0x0002
101 #define TF_SKIPREMOVE 0x0004
103 typedef struct
105 UINT trackFlags;
106 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
107 HMENU hTopMenu; /* initial menu */
108 HWND hOwnerWnd; /* where notifications are sent */
109 POINT pt;
110 } MTRACKER;
112 #define MENU_MAGIC 0x554d /* 'MU' */
114 #define ITEM_PREV -1
115 #define ITEM_NEXT 1
117 /* Internal MENU_TrackMenu() flags */
118 #define TPM_INTERNAL 0xF0000000
119 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
120 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
121 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
123 /* popup menu shade thickness */
124 #define POPUP_XSHADE 4
125 #define POPUP_YSHADE 4
127 /* Space between 2 menu bar items */
128 #define MENU_BAR_ITEMS_SPACE 12
130 /* Minimum width of a tab character */
131 #define MENU_TAB_SPACE 8
133 /* Height of a separator item */
134 #define SEPARATOR_HEIGHT 5
136 /* (other menu->FocusedItem values give the position of the focused item) */
137 #define NO_SELECTED_ITEM 0xffff
139 #define MENU_ITEM_TYPE(flags) \
140 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
142 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
143 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
144 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
146 #define IS_SYSTEM_MENU(menu) \
147 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
149 #define IS_SYSTEM_POPUP(menu) \
150 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
152 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
153 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
154 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
155 MF_POPUP | MF_SYSMENU | MF_HELP)
156 #define STATE_MASK (~TYPE_MASK)
158 /* Dimension of the menu bitmaps */
159 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
161 static HBITMAP hStdMnArrow = 0;
162 static HBITMAP hBmpSysMenu = 0;
164 static HBRUSH hShadeBrush = 0;
165 static HFONT hMenuFont = 0;
166 static HFONT hMenuFontBold = 0;
168 static HMENU MENU_DefSysPopup = 0; /* Default system menu popup */
170 /* Use global popup window because there's no way 2 menus can
171 * be tracked at the same time. */
172 static HWND top_popup;
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 );
179 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
181 /*********************************************************************
182 * menu class descriptor
184 const struct builtin_class_descr MENU_builtin_class =
186 POPUPMENU_CLASS_ATOMA, /* name */
187 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
188 NULL, /* procA (winproc is Unicode only) */
189 PopupMenuWndProc, /* procW */
190 sizeof(HMENU), /* extra */
191 IDC_ARROW, /* cursor */
192 (HBRUSH)(COLOR_MENU+1) /* brush */
196 /***********************************************************************
197 * debug_print_menuitem
199 * Print a menuitem in readable form.
202 #define debug_print_menuitem(pre, mp, post) \
203 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
205 #define MENUOUT(text) \
206 DPRINTF("%s%s", (count++ ? "," : ""), (text))
208 #define MENUFLAG(bit,text) \
209 do { \
210 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
211 } while (0)
213 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
214 const char *postfix)
216 TRACE("%s ", prefix);
217 if (mp) {
218 UINT flags = mp->fType;
219 int type = MENU_ITEM_TYPE(flags);
220 DPRINTF( "{ ID=0x%x", mp->wID);
221 if (flags & MF_POPUP)
222 DPRINTF( ", Sub=%p", mp->hSubMenu);
223 if (flags) {
224 int count = 0;
225 DPRINTF( ", Type=");
226 if (type == MFT_STRING)
227 /* Nothing */ ;
228 else if (type == MFT_SEPARATOR)
229 MENUOUT("sep");
230 else if (type == MFT_OWNERDRAW)
231 MENUOUT("own");
232 else if (type == MFT_BITMAP)
233 MENUOUT("bit");
234 else
235 MENUOUT("???");
236 flags -= type;
238 MENUFLAG(MF_POPUP, "pop");
239 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
240 MENUFLAG(MFT_MENUBREAK, "brk");
241 MENUFLAG(MFT_RADIOCHECK, "radio");
242 MENUFLAG(MFT_RIGHTORDER, "rorder");
243 MENUFLAG(MF_SYSMENU, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
246 if (flags)
247 DPRINTF( "+0x%x", flags);
249 flags = mp->fState;
250 if (flags) {
251 int count = 0;
252 DPRINTF( ", State=");
253 MENUFLAG(MFS_GRAYED, "grey");
254 MENUFLAG(MFS_DEFAULT, "default");
255 MENUFLAG(MFS_DISABLED, "dis");
256 MENUFLAG(MFS_CHECKED, "check");
257 MENUFLAG(MFS_HILITE, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
259 MENUFLAG(MF_MOUSESELECT, "mouse");
260 if (flags)
261 DPRINTF( "+0x%x", flags);
263 if (mp->hCheckBit)
264 DPRINTF( ", Chk=%p", mp->hCheckBit);
265 if (mp->hUnCheckBit)
266 DPRINTF( ", Unc=%p", mp->hUnCheckBit);
268 if (type == MFT_STRING) {
269 if (mp->text)
270 DPRINTF( ", Text=%s", debugstr_w(mp->text));
271 else
272 DPRINTF( ", Text=Null");
273 } else if (mp->text == NULL)
274 /* Nothing */ ;
275 else
276 DPRINTF( ", Text=%p", mp->text);
277 if (mp->dwItemData)
278 DPRINTF( ", ItemData=0x%08lx", mp->dwItemData);
279 DPRINTF( " }");
280 } else {
281 DPRINTF( "NULL");
284 DPRINTF(" %s\n", postfix);
287 #undef MENUOUT
288 #undef MENUFLAG
291 /***********************************************************************
292 * MENU_GetMenu
294 * Validate the given menu handle and returns the menu structure pointer.
296 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
298 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
299 if (!menu || menu->wMagic != MENU_MAGIC)
301 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
302 menu = NULL;
304 return menu;
307 /***********************************************************************
308 * get_win_sys_menu
310 * Get the system menu of a window
312 static HMENU get_win_sys_menu( HWND hwnd )
314 HMENU ret = 0;
315 WND *win = WIN_FindWndPtr( hwnd );
316 if (win)
318 ret = win->hSysMenu;
319 WIN_ReleaseWndPtr( win );
321 return ret;
324 /***********************************************************************
325 * MENU_CopySysPopup
327 * Return the default system menu.
329 static HMENU MENU_CopySysPopup(void)
331 static const WCHAR user32W[] = {'U','S','E','R','3','2',0};
332 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
333 HMENU hMenu = LoadMenuW(GetModuleHandleW(user32W), sysmenuW);
335 if( hMenu ) {
336 POPUPMENU* menu = MENU_GetMenu(hMenu);
337 menu->wFlags |= MF_SYSMENU | MF_POPUP;
338 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
340 else
341 ERR("Unable to load default system menu\n" );
343 TRACE("returning %p.\n", hMenu );
345 return hMenu;
349 /**********************************************************************
350 * MENU_GetSysMenu
352 * Create a copy of the system menu. System menu in Windows is
353 * a special menu bar with the single entry - system menu popup.
354 * This popup is presented to the outside world as a "system menu".
355 * However, the real system menu handle is sometimes seen in the
356 * WM_MENUSELECT parameters (and Word 6 likes it this way).
358 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
360 HMENU hMenu;
362 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
363 if ((hMenu = CreateMenu()))
365 POPUPMENU *menu = MENU_GetMenu(hMenu);
366 menu->wFlags = MF_SYSMENU;
367 menu->hWnd = WIN_GetFullHandle( hWnd );
368 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
370 if (hPopupMenu == (HMENU)(-1))
371 hPopupMenu = MENU_CopySysPopup();
372 else if( !hPopupMenu ) hPopupMenu = MENU_DefSysPopup;
374 if (hPopupMenu)
376 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
377 (UINT_PTR)hPopupMenu, NULL );
379 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
380 menu->items[0].fState = 0;
381 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
383 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
384 return hMenu;
386 DestroyMenu( hMenu );
388 ERR("failed to load system menu!\n");
389 return 0;
393 /***********************************************************************
394 * MENU_Init
396 * Menus initialisation.
398 BOOL MENU_Init(void)
400 HBITMAP hBitmap;
401 NONCLIENTMETRICSW ncm;
403 static unsigned char shade_bits[16] = { 0x55, 0, 0xAA, 0,
404 0x55, 0, 0xAA, 0,
405 0x55, 0, 0xAA, 0,
406 0x55, 0, 0xAA, 0 };
408 /* Load menu bitmaps */
409 hStdMnArrow = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
410 /* Load system buttons bitmaps */
411 hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
413 if (hStdMnArrow)
415 BITMAP bm;
416 GetObjectW( hStdMnArrow, sizeof(bm), &bm );
417 arrow_bitmap_width = bm.bmWidth;
418 arrow_bitmap_height = bm.bmHeight;
419 } else
420 return FALSE;
422 if (! (hBitmap = CreateBitmap( 8, 8, 1, 1, shade_bits)))
423 return FALSE;
425 if(!(hShadeBrush = CreatePatternBrush( hBitmap )))
426 return FALSE;
428 DeleteObject( hBitmap );
429 if (!(MENU_DefSysPopup = MENU_CopySysPopup()))
430 return FALSE;
432 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
433 if (!(SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0)))
434 return FALSE;
436 if (!(hMenuFont = CreateFontIndirectW( &ncm.lfMenuFont )))
437 return FALSE;
439 ncm.lfMenuFont.lfWeight += 300;
440 if ( ncm.lfMenuFont.lfWeight > 1000)
441 ncm.lfMenuFont.lfWeight = 1000;
443 if (!(hMenuFontBold = CreateFontIndirectW( &ncm.lfMenuFont )))
444 return FALSE;
446 return TRUE;
449 /***********************************************************************
450 * MENU_InitSysMenuPopup
452 * Grey the appropriate items in System menu.
454 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
456 BOOL gray;
458 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
459 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
460 gray = ((style & WS_MAXIMIZE) != 0);
461 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
462 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
463 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
464 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
465 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
466 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
467 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
468 gray = (clsStyle & CS_NOCLOSE) != 0;
470 /* The menu item must keep its state if it's disabled */
471 if(gray)
472 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
476 /******************************************************************************
478 * UINT MENU_GetStartOfNextColumn(
479 * HMENU hMenu )
481 *****************************************************************************/
483 static UINT MENU_GetStartOfNextColumn(
484 HMENU hMenu )
486 POPUPMENU *menu = MENU_GetMenu(hMenu);
487 UINT i;
489 if(!menu)
490 return NO_SELECTED_ITEM;
492 i = menu->FocusedItem + 1;
493 if( i == NO_SELECTED_ITEM )
494 return i;
496 for( ; i < menu->nItems; ++i ) {
497 if (menu->items[i].fType & MF_MENUBARBREAK)
498 return i;
501 return NO_SELECTED_ITEM;
505 /******************************************************************************
507 * UINT MENU_GetStartOfPrevColumn(
508 * HMENU hMenu )
510 *****************************************************************************/
512 static UINT MENU_GetStartOfPrevColumn(
513 HMENU hMenu )
515 POPUPMENU *menu = MENU_GetMenu(hMenu);
516 UINT i;
518 if( !menu )
519 return NO_SELECTED_ITEM;
521 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
522 return NO_SELECTED_ITEM;
524 /* Find the start of the column */
526 for(i = menu->FocusedItem; i != 0 &&
527 !(menu->items[i].fType & MF_MENUBARBREAK);
528 --i); /* empty */
530 if(i == 0)
531 return NO_SELECTED_ITEM;
533 for(--i; i != 0; --i) {
534 if (menu->items[i].fType & MF_MENUBARBREAK)
535 break;
538 TRACE("ret %d.\n", i );
540 return i;
545 /***********************************************************************
546 * MENU_FindItem
548 * Find a menu item. Return a pointer on the item, and modifies *hmenu
549 * in case the item was in a sub-menu.
551 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
553 POPUPMENU *menu;
554 UINT i;
556 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
557 if (wFlags & MF_BYPOSITION)
559 if (*nPos >= menu->nItems) return NULL;
560 return &menu->items[*nPos];
562 else
564 MENUITEM *item = menu->items;
565 for (i = 0; i < menu->nItems; i++, item++)
567 if (item->wID == *nPos)
569 *nPos = i;
570 return item;
572 else if (item->fType & MF_POPUP)
574 HMENU hsubmenu = item->hSubMenu;
575 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
576 if (subitem)
578 *hmenu = hsubmenu;
579 return subitem;
584 return NULL;
587 /***********************************************************************
588 * MENU_FindSubMenu
590 * Find a Sub menu. Return the position of the submenu, and modifies
591 * *hmenu in case it is found in another sub-menu.
592 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
594 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
596 POPUPMENU *menu;
597 UINT i;
598 MENUITEM *item;
599 if (((*hmenu)==(HMENU)0xffff) ||
600 (!(menu = MENU_GetMenu(*hmenu))))
601 return NO_SELECTED_ITEM;
602 item = menu->items;
603 for (i = 0; i < menu->nItems; i++, item++) {
604 if(!(item->fType & MF_POPUP)) continue;
605 if (item->hSubMenu == hSubTarget) {
606 return i;
608 else {
609 HMENU hsubmenu = item->hSubMenu;
610 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
611 if (pos != NO_SELECTED_ITEM) {
612 *hmenu = hsubmenu;
613 return pos;
617 return NO_SELECTED_ITEM;
620 /***********************************************************************
621 * MENU_FreeItemData
623 static void MENU_FreeItemData( MENUITEM* item )
625 /* delete text */
626 if (IS_STRING_ITEM(item->fType) && item->text)
627 HeapFree( GetProcessHeap(), 0, item->text );
630 /***********************************************************************
631 * MENU_FindItemByCoords
633 * Find the item at the specified coordinates (screen coords). Does
634 * not work for child windows and therefore should not be called for
635 * an arbitrary system menu.
637 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu,
638 POINT pt, UINT *pos )
640 MENUITEM *item;
641 UINT i;
642 RECT wrect;
644 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
645 pt.x -= wrect.left;pt.y -= wrect.top;
646 item = menu->items;
647 for (i = 0; i < menu->nItems; i++, item++)
649 if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
650 (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
652 if (pos) *pos = i;
653 return item;
656 return NULL;
660 /***********************************************************************
661 * MENU_FindItemByKey
663 * Find the menu item selected by a key press.
664 * Return item id, -1 if none, -2 if we should close the menu.
666 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
667 WCHAR key, BOOL forceMenuChar )
669 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
671 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
673 if (hmenu)
675 POPUPMENU *menu = MENU_GetMenu( hmenu );
676 MENUITEM *item = menu->items;
677 LRESULT menuchar;
679 if( !forceMenuChar )
681 UINT i;
683 key = toupperW(key);
684 for (i = 0; i < menu->nItems; i++, item++)
686 if (IS_STRING_ITEM(item->fType) && item->text)
688 WCHAR *p = item->text - 2;
691 p = strchrW (p + 2, '&');
693 while (p != NULL && p [1] == '&');
694 if (p && (toupperW(p[1]) == key)) return i;
698 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
699 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
700 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
701 if (HIWORD(menuchar) == 1) return (UINT)(-2);
703 return (UINT)(-1);
707 /***********************************************************************
708 * MENU_GetBitmapItemSize
710 * Get the size of a bitmap item.
712 static void MENU_GetBitmapItemSize( UINT id, DWORD data, SIZE *size )
714 BITMAP bm;
715 HBITMAP bmp = (HBITMAP)id;
717 size->cx = size->cy = 0;
719 /* check if there is a magic menu item associated with this item */
720 if (id && IS_MAGIC_ITEM( id ))
722 switch(LOWORD(id))
724 case (INT_PTR)HBMMENU_SYSTEM:
725 if (data)
727 bmp = (HBITMAP)data;
728 break;
730 /* fall through */
731 case (INT_PTR)HBMMENU_MBAR_RESTORE:
732 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
733 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
734 case (INT_PTR)HBMMENU_MBAR_CLOSE:
735 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
736 size->cx = GetSystemMetrics( SM_CXSIZE );
737 size->cy = GetSystemMetrics( SM_CYSIZE );
738 return;
739 case (INT_PTR)HBMMENU_CALLBACK:
740 case (INT_PTR)HBMMENU_POPUP_CLOSE:
741 case (INT_PTR)HBMMENU_POPUP_RESTORE:
742 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
743 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
744 default:
745 FIXME("Magic 0x%08x not implemented\n", id);
746 return;
749 if (GetObjectW(bmp, sizeof(bm), &bm ))
751 size->cx = bm.bmWidth;
752 size->cy = bm.bmHeight;
756 /***********************************************************************
757 * MENU_DrawBitmapItem
759 * Draw a bitmap item.
761 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect, BOOL menuBar )
763 BITMAP bm;
764 DWORD rop;
765 HDC hdcMem;
766 HBITMAP bmp = (HBITMAP)lpitem->text;
767 int w = rect->right - rect->left;
768 int h = rect->bottom - rect->top;
769 int bmp_xoffset = 0;
770 int left, top;
772 /* Check if there is a magic menu item associated with this item */
773 if (lpitem->text && IS_MAGIC_ITEM(lpitem->text))
775 UINT flags = 0;
776 RECT r;
778 switch(LOWORD(lpitem->text))
780 case (INT_PTR)HBMMENU_SYSTEM:
781 if (lpitem->dwItemData)
783 bmp = (HBITMAP)lpitem->dwItemData;
784 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
786 else
788 bmp = hBmpSysMenu;
789 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
790 /* only use right half of the bitmap */
791 bmp_xoffset = bm.bmWidth / 2;
792 bm.bmWidth -= bmp_xoffset;
794 goto got_bitmap;
795 case (INT_PTR)HBMMENU_MBAR_RESTORE:
796 flags = DFCS_CAPTIONRESTORE;
797 break;
798 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
799 flags = DFCS_CAPTIONMIN;
800 break;
801 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
802 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
803 break;
804 case (INT_PTR)HBMMENU_MBAR_CLOSE:
805 flags = DFCS_CAPTIONCLOSE;
806 break;
807 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
808 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
809 break;
810 case (INT_PTR)HBMMENU_CALLBACK:
811 case (INT_PTR)HBMMENU_POPUP_CLOSE:
812 case (INT_PTR)HBMMENU_POPUP_RESTORE:
813 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
814 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
815 default:
816 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem->text));
817 return;
819 r = *rect;
820 InflateRect( &r, -1, -1 );
821 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
822 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
823 return;
826 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
828 got_bitmap:
829 hdcMem = CreateCompatibleDC( hdc );
830 SelectObject( hdcMem, bmp );
832 /* handle fontsize > bitmap_height */
833 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
834 left=rect->left;
835 if (TWEAK_WineLook == WIN95_LOOK) {
836 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text)) ? NOTSRCCOPY : SRCCOPY;
837 if ((lpitem->fState & MF_HILITE) && IS_BITMAP_ITEM(lpitem->fType))
838 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
839 } else {
840 left++;
841 w-=2;
842 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_ITEM(lpitem->text) && (!menuBar)) ? MERGEPAINT : SRCCOPY;
844 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
845 DeleteDC( hdcMem );
849 /***********************************************************************
850 * MENU_CalcItemSize
852 * Calculate the size of the menu item and store it in lpitem->rect.
854 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
855 INT orgX, INT orgY, BOOL menuBar )
857 WCHAR *p;
858 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
860 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
861 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
862 (menuBar ? " (MenuBar)" : ""));
864 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
866 if (lpitem->fType & MF_OWNERDRAW)
869 ** Experimentation under Windows reveals that an owner-drawn
870 ** menu is expected to return the size of the content part of
871 ** the menu item, not including the checkmark nor the submenu
872 ** arrow. Windows adds those values itself and returns the
873 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
875 MEASUREITEMSTRUCT mis;
876 mis.CtlType = ODT_MENU;
877 mis.CtlID = 0;
878 mis.itemID = lpitem->wID;
879 mis.itemData = (DWORD)lpitem->dwItemData;
880 mis.itemHeight = 0;
881 mis.itemWidth = 0;
882 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
883 lpitem->rect.right += mis.itemWidth;
885 if (menuBar)
887 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
890 /* under at least win95 you seem to be given a standard
891 height for the menu and the height value is ignored */
893 if (TWEAK_WineLook == WIN31_LOOK)
894 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU);
895 else
896 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENU)-1;
898 else
899 lpitem->rect.bottom += mis.itemHeight;
901 TRACE("id=%04x size=%dx%d\n",
902 lpitem->wID, mis.itemWidth, mis.itemHeight);
903 /* Fall through to get check/arrow width calculation. */
906 if (lpitem->fType & MF_SEPARATOR)
908 lpitem->rect.bottom += SEPARATOR_HEIGHT;
909 return;
912 if (!menuBar)
914 lpitem->rect.right += 2 * check_bitmap_width;
915 if (lpitem->fType & MF_POPUP)
916 lpitem->rect.right += arrow_bitmap_width;
919 if (lpitem->fType & MF_OWNERDRAW)
920 return;
922 if (IS_BITMAP_ITEM(lpitem->fType))
924 SIZE size;
926 MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size );
927 lpitem->rect.right += size.cx;
928 lpitem->rect.bottom += size.cy;
929 if (TWEAK_WineLook == WIN98_LOOK)
931 /* Leave space for the sunken border */
932 lpitem->rect.right += 2;
933 lpitem->rect.bottom += 2;
938 /* it must be a text item - unless it's the system menu */
939 if (!(lpitem->fType & MF_SYSMENU) && IS_STRING_ITEM( lpitem->fType ))
940 { SIZE size;
942 GetTextExtentPoint32W(hdc, lpitem->text, strlenW(lpitem->text), &size);
944 lpitem->rect.right += size.cx;
945 if (TWEAK_WineLook == WIN31_LOOK)
946 lpitem->rect.bottom += max( size.cy, GetSystemMetrics(SM_CYMENU) );
947 else
948 lpitem->rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU)-1);
949 lpitem->xTab = 0;
951 if (menuBar)
953 lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
955 else if ((p = strchrW( lpitem->text, '\t' )) != NULL)
957 /* Item contains a tab (only meaningful in popup menus) */
958 GetTextExtentPoint32W(hdc, lpitem->text, (int)(p - lpitem->text) , &size);
959 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE + size.cx;
960 lpitem->rect.right += MENU_TAB_SPACE;
962 else
964 if (strchrW( lpitem->text, '\b' ))
965 lpitem->rect.right += MENU_TAB_SPACE;
966 lpitem->xTab = lpitem->rect.right - check_bitmap_width
967 - arrow_bitmap_width;
970 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->rect.left, lpitem->rect.top, lpitem->rect.right, lpitem->rect.bottom);
974 /***********************************************************************
975 * MENU_PopupMenuCalcSize
977 * Calculate the size of a popup menu.
979 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
981 MENUITEM *lpitem;
982 HDC hdc;
983 int start, i;
984 int orgX, orgY, maxX, maxTab, maxTabWidth;
986 lppop->Width = lppop->Height = 0;
987 if (lppop->nItems == 0) return;
988 hdc = GetDC( 0 );
990 SelectObject( hdc, hMenuFont);
992 start = 0;
993 maxX = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CXBORDER) : 2+1 ;
995 while (start < lppop->nItems)
997 lpitem = &lppop->items[start];
998 orgX = maxX;
999 orgY = (TWEAK_WineLook == WIN31_LOOK) ? GetSystemMetrics(SM_CYBORDER) : 2;
1001 maxTab = maxTabWidth = 0;
1003 /* Parse items until column break or end of menu */
1004 for (i = start; i < lppop->nItems; i++, lpitem++)
1006 if ((i != start) &&
1007 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1009 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
1011 if (lpitem->fType & MF_MENUBARBREAK) orgX++;
1012 maxX = max( maxX, lpitem->rect.right );
1013 orgY = lpitem->rect.bottom;
1014 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1016 maxTab = max( maxTab, lpitem->xTab );
1017 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1021 /* Finish the column (set all items to the largest width found) */
1022 maxX = max( maxX, maxTab + maxTabWidth );
1023 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1025 lpitem->rect.right = maxX;
1026 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1027 lpitem->xTab = maxTab;
1030 lppop->Height = max( lppop->Height, orgY );
1033 lppop->Width = maxX;
1035 /* space for 3d border */
1036 if(TWEAK_WineLook > WIN31_LOOK)
1038 lppop->Height += 2;
1039 lppop->Width += 2;
1042 ReleaseDC( 0, hdc );
1046 /***********************************************************************
1047 * MENU_MenuBarCalcSize
1049 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1050 * height is off by 1 pixel which causes lengthy window relocations when
1051 * active document window is maximized/restored.
1053 * Calculate the size of the menu bar.
1055 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1056 LPPOPUPMENU lppop, HWND hwndOwner )
1058 MENUITEM *lpitem;
1059 int start, i, orgX, orgY, maxY, helpPos;
1061 if ((lprect == NULL) || (lppop == NULL)) return;
1062 if (lppop->nItems == 0) return;
1063 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1064 lprect->left, lprect->top, lprect->right, lprect->bottom);
1065 lppop->Width = lprect->right - lprect->left;
1066 lppop->Height = 0;
1067 maxY = lprect->top+1;
1068 start = 0;
1069 helpPos = -1;
1070 while (start < lppop->nItems)
1072 lpitem = &lppop->items[start];
1073 orgX = lprect->left;
1074 orgY = maxY;
1076 /* Parse items until line break or end of menu */
1077 for (i = start; i < lppop->nItems; i++, lpitem++)
1079 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1080 if ((i != start) &&
1081 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1083 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1084 orgX, orgY );
1085 debug_print_menuitem (" item: ", lpitem, "");
1086 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
1088 if (lpitem->rect.right > lprect->right)
1090 if (i != start) break;
1091 else lpitem->rect.right = lprect->right;
1093 maxY = max( maxY, lpitem->rect.bottom );
1094 orgX = lpitem->rect.right;
1097 /* Finish the line (set all items to the largest height found) */
1098 while (start < i) lppop->items[start++].rect.bottom = maxY;
1101 lprect->bottom = maxY;
1102 lppop->Height = lprect->bottom - lprect->top;
1104 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1105 /* the last item (if several lines, only move the last line) */
1106 lpitem = &lppop->items[lppop->nItems-1];
1107 orgY = lpitem->rect.top;
1108 orgX = lprect->right;
1109 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1110 if ( (helpPos==-1) || (helpPos>i) )
1111 break; /* done */
1112 if (lpitem->rect.top != orgY) break; /* Other line */
1113 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1114 lpitem->rect.left += orgX - lpitem->rect.right;
1115 lpitem->rect.right = orgX;
1116 orgX = lpitem->rect.left;
1120 /***********************************************************************
1121 * MENU_DrawMenuItem
1123 * Draw a single menu item.
1125 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1126 UINT height, BOOL menuBar, UINT odaction )
1128 RECT rect;
1130 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1132 if (lpitem->fType & MF_SYSMENU)
1134 if( !IsIconic(hwnd) ) {
1135 if (TWEAK_WineLook > WIN31_LOOK)
1136 NC_DrawSysButton95( hwnd, hdc,
1137 lpitem->fState &
1138 (MF_HILITE | MF_MOUSESELECT) );
1139 else
1140 NC_DrawSysButton( hwnd, hdc,
1141 lpitem->fState &
1142 (MF_HILITE | MF_MOUSESELECT) );
1145 return;
1148 if (lpitem->fType & MF_OWNERDRAW)
1151 ** Experimentation under Windows reveals that an owner-drawn
1152 ** menu is given the rectangle which includes the space it requested
1153 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1154 ** and a popup-menu arrow. This is the value of lpitem->rect.
1155 ** Windows will leave all drawing to the application except for
1156 ** the popup-menu arrow. Windows always draws that itself, after
1157 ** the menu owner has finished drawing.
1159 DRAWITEMSTRUCT dis;
1161 dis.CtlType = ODT_MENU;
1162 dis.CtlID = 0;
1163 dis.itemID = lpitem->wID;
1164 dis.itemData = (DWORD)lpitem->dwItemData;
1165 dis.itemState = 0;
1166 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1167 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1168 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1169 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1170 dis.hwndItem = (HWND)hmenu;
1171 dis.hDC = hdc;
1172 dis.rcItem = lpitem->rect;
1173 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1174 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner,
1175 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1176 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1177 dis.rcItem.bottom);
1178 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1179 /* Fall through to draw popup-menu arrow */
1182 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem->rect.left, lpitem->rect.top,
1183 lpitem->rect.right,lpitem->rect.bottom);
1185 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1187 rect = lpitem->rect;
1189 if (!(lpitem->fType & MF_OWNERDRAW))
1191 if (lpitem->fState & MF_HILITE)
1193 if(TWEAK_WineLook == WIN98_LOOK)
1195 if(menuBar)
1196 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1197 else
1198 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1200 else /* Not Win98 Look */
1202 if(!IS_BITMAP_ITEM(lpitem->fType))
1203 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1206 else
1207 FillRect( hdc, &rect, GetSysColorBrush(COLOR_MENU) );
1210 SetBkMode( hdc, TRANSPARENT );
1212 if (!(lpitem->fType & MF_OWNERDRAW))
1214 /* vertical separator */
1215 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1217 if (TWEAK_WineLook > WIN31_LOOK)
1219 RECT rc = rect;
1220 rc.top = 3;
1221 rc.bottom = height - 3;
1222 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1224 else
1226 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1227 MoveToEx( hdc, rect.left, 0, NULL );
1228 LineTo( hdc, rect.left, height );
1232 /* horizontal separator */
1233 if (lpitem->fType & MF_SEPARATOR)
1235 if (TWEAK_WineLook > WIN31_LOOK)
1237 RECT rc = rect;
1238 rc.left++;
1239 rc.right--;
1240 rc.top += SEPARATOR_HEIGHT / 2;
1241 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1243 else
1245 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1246 MoveToEx( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2, NULL );
1247 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
1249 return;
1253 /* Setup colors */
1255 if (lpitem->fState & MF_HILITE)
1257 if(TWEAK_WineLook == WIN98_LOOK)
1259 if(menuBar) {
1260 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1261 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1262 } else {
1263 if(lpitem->fState & MF_GRAYED)
1264 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1265 else
1266 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1267 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1270 else /* Not Win98 Look */
1272 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1273 if(!IS_BITMAP_ITEM(lpitem->fType))
1274 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1277 else
1279 if (lpitem->fState & MF_GRAYED)
1280 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1281 else
1282 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1283 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
1286 /* helper lines for debugging */
1287 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1288 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1289 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1290 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1293 if (!menuBar)
1295 INT y = rect.top + rect.bottom;
1296 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1297 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1299 if (!(lpitem->fType & MF_OWNERDRAW))
1301 /* Draw the check mark
1303 * FIXME:
1304 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1306 HBITMAP bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit : lpitem->hUnCheckBit;
1307 if (bm) /* we have a custom bitmap */
1309 HDC hdcMem = CreateCompatibleDC( hdc );
1310 SelectObject( hdcMem, bm );
1311 BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
1312 check_bitmap_width, check_bitmap_height,
1313 hdcMem, 0, 0, SRCCOPY );
1314 DeleteDC( hdcMem );
1316 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1318 RECT r;
1319 HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
1320 HDC hdcMem = CreateCompatibleDC( hdc );
1321 SelectObject( hdcMem, bm );
1322 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height );
1323 DrawFrameControl( hdcMem, &r, DFC_MENU,
1324 (lpitem->fType & MFT_RADIOCHECK) ?
1325 DFCS_MENUBULLET : DFCS_MENUCHECK );
1326 BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
1327 hdcMem, 0, 0, SRCCOPY );
1328 DeleteDC( hdcMem );
1329 DeleteObject( bm );
1333 /* Draw the popup-menu arrow */
1334 if (lpitem->fType & MF_POPUP)
1336 HDC hdcMem = CreateCompatibleDC( hdc );
1337 HBITMAP hOrigBitmap;
1339 hOrigBitmap = SelectObject( hdcMem, hStdMnArrow );
1340 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1341 (y - arrow_bitmap_height) / 2,
1342 arrow_bitmap_width, arrow_bitmap_height,
1343 hdcMem, 0, 0, SRCCOPY );
1344 SelectObject( hdcMem, hOrigBitmap );
1345 DeleteDC( hdcMem );
1348 rect.left += check_bitmap_width;
1349 rect.right -= arrow_bitmap_width;
1352 /* Done for owner-drawn */
1353 if (lpitem->fType & MF_OWNERDRAW)
1354 return;
1356 /* Draw the item text or bitmap */
1357 if (IS_BITMAP_ITEM(lpitem->fType))
1359 MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar );
1360 return;
1363 /* No bitmap - process text if present */
1364 else if (IS_STRING_ITEM(lpitem->fType))
1366 register int i;
1367 HFONT hfontOld = 0;
1369 UINT uFormat = (menuBar) ?
1370 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1371 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1373 if ( lpitem->fState & MFS_DEFAULT )
1375 hfontOld = SelectObject( hdc, hMenuFontBold);
1378 if (menuBar)
1380 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1381 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1384 for (i = 0; lpitem->text[i]; i++)
1385 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1386 break;
1388 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1390 if (!(lpitem->fState & MF_HILITE) )
1392 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1393 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1394 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1395 --rect.left; --rect.top; --rect.right; --rect.bottom;
1397 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1400 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1402 /* paint the shortcut text */
1403 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1405 if (lpitem->text[i] == '\t')
1407 rect.left = lpitem->xTab;
1408 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1410 else
1412 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1415 if( (TWEAK_WineLook != WIN31_LOOK) && (lpitem->fState & MF_GRAYED))
1417 if (!(lpitem->fState & MF_HILITE) )
1419 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1420 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1421 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1422 --rect.left; --rect.top; --rect.right; --rect.bottom;
1424 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1426 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1429 if (hfontOld)
1430 SelectObject (hdc, hfontOld);
1435 /***********************************************************************
1436 * MENU_DrawPopupMenu
1438 * Paint a popup menu.
1440 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1442 HBRUSH hPrevBrush = 0;
1443 RECT rect;
1445 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1447 GetClientRect( hwnd, &rect );
1449 if(TWEAK_WineLook == WIN31_LOOK)
1451 rect.bottom -= POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1452 rect.right -= POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER);
1455 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1456 && (SelectObject( hdc, hMenuFont)))
1458 HPEN hPrevPen;
1460 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1462 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1463 if( hPrevPen )
1465 INT ropPrev, i;
1466 POPUPMENU *menu;
1468 /* draw 3-d shade */
1469 if(TWEAK_WineLook == WIN31_LOOK) {
1470 SelectObject( hdc, hShadeBrush );
1471 SetBkMode( hdc, TRANSPARENT );
1472 ropPrev = SetROP2( hdc, R2_MASKPEN );
1474 i = rect.right; /* why SetBrushOrg() doesn't? */
1475 PatBlt( hdc, i & 0xfffffffe,
1476 rect.top + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER),
1477 i%2 + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1478 rect.bottom - rect.top, 0x00a000c9 );
1479 i = rect.bottom;
1480 PatBlt( hdc, rect.left + POPUP_XSHADE*GetSystemMetrics(SM_CXBORDER),
1481 i & 0xfffffffe,rect.right - rect.left,
1482 i%2 + POPUP_YSHADE*GetSystemMetrics(SM_CYBORDER), 0x00a000c9 );
1483 SelectObject( hdc, hPrevPen );
1484 SelectObject( hdc, hPrevBrush );
1485 SetROP2( hdc, ropPrev );
1487 else
1488 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1490 /* draw menu items */
1492 menu = MENU_GetMenu( hmenu );
1493 if (menu && menu->nItems)
1495 MENUITEM *item;
1496 UINT u;
1498 for (u = menu->nItems, item = menu->items; u > 0; u--, item++)
1499 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc, item,
1500 menu->Height, FALSE, ODA_DRAWENTIRE );
1503 } else
1505 SelectObject( hdc, hPrevBrush );
1510 /***********************************************************************
1511 * MENU_DrawMenuBar
1513 * Paint a menu bar. Returns the height of the menu bar.
1514 * called from [windows/nonclient.c]
1516 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1517 BOOL suppress_draw)
1519 LPPOPUPMENU lppop;
1520 HFONT hfontOld = 0;
1521 HMENU hMenu = GetMenu(hwnd);
1523 lppop = MENU_GetMenu( hMenu );
1524 if (lppop == NULL || lprect == NULL)
1526 return GetSystemMetrics(SM_CYMENU);
1529 if (suppress_draw)
1531 hfontOld = SelectObject( hDC, hMenuFont);
1533 if (lppop->Height == 0)
1534 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1536 lprect->bottom = lprect->top + lppop->Height;
1538 if (hfontOld) SelectObject( hDC, hfontOld);
1539 return lppop->Height;
1541 else
1542 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1546 /***********************************************************************
1547 * MENU_ShowPopup
1549 * Display a popup menu.
1551 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1552 INT x, INT y, INT xanchor, INT yanchor )
1554 POPUPMENU *menu;
1555 UINT width, height;
1557 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1558 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1560 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1561 if (menu->FocusedItem != NO_SELECTED_ITEM)
1563 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1564 menu->FocusedItem = NO_SELECTED_ITEM;
1567 /* store the owner for DrawItem */
1568 menu->hwndOwner = hwndOwner;
1571 MENU_PopupMenuCalcSize( menu, hwndOwner );
1573 /* adjust popup menu pos so that it fits within the desktop */
1575 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1576 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1578 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1580 if( xanchor )
1581 x -= width - xanchor;
1582 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1583 x = GetSystemMetrics(SM_CXSCREEN) - width;
1585 if( x < 0 ) x = 0;
1587 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1589 if( yanchor )
1590 y -= height + yanchor;
1591 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1592 y = GetSystemMetrics(SM_CYSCREEN) - height;
1594 if( y < 0 ) y = 0;
1596 if( TWEAK_WineLook == WIN31_LOOK )
1598 width += POPUP_XSHADE * GetSystemMetrics(SM_CXBORDER); /* add space for shading */
1599 height += POPUP_YSHADE * GetSystemMetrics(SM_CYBORDER);
1602 /* NOTE: In Windows, top menu popup is not owned. */
1603 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1604 WS_POPUP, x, y, width, height,
1605 hwndOwner, 0, (HINSTANCE)GetWindowLongW(hwndOwner, GWL_HINSTANCE),
1606 (LPVOID)hmenu );
1607 if( !menu->hWnd ) return FALSE;
1608 if (!top_popup) top_popup = menu->hWnd;
1610 /* Display the window */
1612 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1613 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1614 UpdateWindow( menu->hWnd );
1615 return TRUE;
1619 /***********************************************************************
1620 * MENU_SelectItem
1622 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1623 BOOL sendMenuSelect, HMENU topmenu )
1625 LPPOPUPMENU lppop;
1626 HDC hdc;
1628 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1630 lppop = MENU_GetMenu( hmenu );
1631 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1633 if (lppop->FocusedItem == wIndex) return;
1634 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1635 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1636 if (!top_popup) top_popup = lppop->hWnd;
1638 SelectObject( hdc, hMenuFont);
1640 /* Clear previous highlighted item */
1641 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1643 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1644 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1645 lppop->Height, !(lppop->wFlags & MF_POPUP),
1646 ODA_SELECT );
1649 /* Highlight new item (if any) */
1650 lppop->FocusedItem = wIndex;
1651 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1653 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1654 lppop->items[wIndex].fState |= MF_HILITE;
1655 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1656 &lppop->items[wIndex], lppop->Height,
1657 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1659 if (sendMenuSelect)
1661 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1662 SendMessageW( hwndOwner, WM_MENUSELECT,
1663 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1664 ip->fType | ip->fState | MF_MOUSESELECT |
1665 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1668 else if (sendMenuSelect) {
1669 if(topmenu){
1670 int pos;
1671 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1672 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1673 MENUITEM *ip = &ptm->items[pos];
1674 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1675 ip->fType | ip->fState | MF_MOUSESELECT |
1676 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1680 ReleaseDC( lppop->hWnd, hdc );
1684 /***********************************************************************
1685 * MENU_MoveSelection
1687 * Moves currently selected item according to the offset parameter.
1688 * If there is no selection then it should select the last item if
1689 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1691 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1693 INT i;
1694 POPUPMENU *menu;
1696 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1698 menu = MENU_GetMenu( hmenu );
1699 if ((!menu) || (!menu->items)) return;
1701 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1703 if( menu->nItems == 1 ) return; else
1704 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1705 ; i += offset)
1706 if (!(menu->items[i].fType & MF_SEPARATOR))
1708 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1709 return;
1713 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1714 i >= 0 && i < menu->nItems ; i += offset)
1715 if (!(menu->items[i].fType & MF_SEPARATOR))
1717 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1718 return;
1723 /**********************************************************************
1724 * MENU_SetItemData
1726 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1727 * ModifyMenu().
1729 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1730 LPCWSTR str )
1732 LPWSTR prevText = IS_STRING_ITEM(item->fType) ? item->text : NULL;
1734 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1735 TRACE("flags=%x str=%p\n", flags, str);
1737 if (IS_STRING_ITEM(flags))
1739 if (!str)
1741 flags |= MF_SEPARATOR;
1742 item->text = NULL;
1744 else
1746 LPWSTR text;
1747 /* Item beginning with a backspace is a help item */
1748 if (*str == '\b')
1750 flags |= MF_HELP;
1751 str++;
1753 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
1754 return FALSE;
1755 strcpyW( text, str );
1756 item->text = text;
1759 else if (IS_BITMAP_ITEM(flags))
1760 item->text = (LPWSTR)HBITMAP_32(LOWORD(str));
1761 else item->text = NULL;
1763 if (flags & MF_OWNERDRAW)
1764 item->dwItemData = (DWORD)str;
1765 else
1766 item->dwItemData = 0;
1768 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
1769 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
1771 if (flags & MF_POPUP)
1773 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
1774 if (menu) menu->wFlags |= MF_POPUP;
1775 else
1777 item->wID = 0;
1778 item->hSubMenu = 0;
1779 item->fType = 0;
1780 item->fState = 0;
1781 return FALSE;
1785 item->wID = id;
1786 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
1788 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
1789 flags |= MF_POPUP; /* keep popup */
1791 item->fType = flags & TYPE_MASK;
1792 item->fState = (flags & STATE_MASK) &
1793 ~(MF_HILITE | MF_MOUSESELECT | MF_BYPOSITION);
1796 /* Don't call SetRectEmpty here! */
1799 if (prevText) HeapFree( GetProcessHeap(), 0, prevText );
1801 debug_print_menuitem("MENU_SetItemData to : ", item, "");
1802 return TRUE;
1806 /**********************************************************************
1807 * MENU_InsertItem
1809 * Insert (allocate) a new item into a menu.
1811 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
1813 MENUITEM *newItems;
1814 POPUPMENU *menu;
1816 if (!(menu = MENU_GetMenu(hMenu)))
1817 return NULL;
1819 /* Find where to insert new item */
1821 if (flags & MF_BYPOSITION) {
1822 if (pos > menu->nItems)
1823 pos = menu->nItems;
1824 } else {
1825 if (!MENU_FindItem( &hMenu, &pos, flags ))
1826 pos = menu->nItems;
1827 else {
1828 if (!(menu = MENU_GetMenu( hMenu )))
1829 return NULL;
1833 /* Create new items array */
1835 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
1836 if (!newItems)
1838 WARN("allocation failed\n" );
1839 return NULL;
1841 if (menu->nItems > 0)
1843 /* Copy the old array into the new one */
1844 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
1845 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
1846 (menu->nItems-pos)*sizeof(MENUITEM) );
1847 HeapFree( GetProcessHeap(), 0, menu->items );
1849 menu->items = newItems;
1850 menu->nItems++;
1851 memset( &newItems[pos], 0, sizeof(*newItems) );
1852 menu->Height = 0; /* force size recalculate */
1853 return &newItems[pos];
1857 /**********************************************************************
1858 * MENU_ParseResource
1860 * Parse a standard menu resource and add items to the menu.
1861 * Return a pointer to the end of the resource.
1863 * NOTE: flags is equivalent to the mtOption field
1865 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1867 WORD flags, id = 0;
1868 LPCSTR str;
1872 flags = GET_WORD(res);
1873 res += sizeof(WORD);
1874 if (!(flags & MF_POPUP))
1876 id = GET_WORD(res);
1877 res += sizeof(WORD);
1879 str = res;
1880 if (!unicode) res += strlen(str) + 1;
1881 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1882 if (flags & MF_POPUP)
1884 HMENU hSubMenu = CreatePopupMenu();
1885 if (!hSubMenu) return NULL;
1886 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
1887 return NULL;
1888 if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
1889 else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
1891 else /* Not a popup */
1893 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
1894 else AppendMenuW( hMenu, flags, id,
1895 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
1897 } while (!(flags & MF_END));
1898 return res;
1902 /**********************************************************************
1903 * MENUEX_ParseResource
1905 * Parse an extended menu resource and add items to the menu.
1906 * Return a pointer to the end of the resource.
1908 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1910 WORD resinfo;
1911 do {
1912 MENUITEMINFOW mii;
1914 mii.cbSize = sizeof(mii);
1915 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
1916 mii.fType = GET_DWORD(res);
1917 res += sizeof(DWORD);
1918 mii.fState = GET_DWORD(res);
1919 res += sizeof(DWORD);
1920 mii.wID = GET_DWORD(res);
1921 res += sizeof(DWORD);
1922 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
1923 res += sizeof(WORD);
1924 /* Align the text on a word boundary. */
1925 res += (~((int)res - 1)) & 1;
1926 mii.dwTypeData = (LPWSTR) res;
1927 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1928 /* Align the following fields on a dword boundary. */
1929 res += (~((int)res - 1)) & 3;
1931 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1932 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
1934 if (resinfo & 1) { /* Pop-up? */
1935 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1936 res += sizeof(DWORD);
1937 mii.hSubMenu = CreatePopupMenu();
1938 if (!mii.hSubMenu)
1939 return NULL;
1940 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
1941 DestroyMenu(mii.hSubMenu);
1942 return NULL;
1944 mii.fMask |= MIIM_SUBMENU;
1945 mii.fType |= MF_POPUP;
1947 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1949 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1950 mii.wID, mii.fType);
1951 mii.fType |= MF_SEPARATOR;
1953 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1954 } while (!(resinfo & MF_END));
1955 return res;
1959 /***********************************************************************
1960 * MENU_GetSubPopup
1962 * Return the handle of the selected sub-popup menu (if any).
1964 static HMENU MENU_GetSubPopup( HMENU hmenu )
1966 POPUPMENU *menu;
1967 MENUITEM *item;
1969 menu = MENU_GetMenu( hmenu );
1971 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
1973 item = &menu->items[menu->FocusedItem];
1974 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
1975 return item->hSubMenu;
1976 return 0;
1980 /***********************************************************************
1981 * MENU_HideSubPopups
1983 * Hide the sub-popup menus of this menu.
1985 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
1986 BOOL sendMenuSelect )
1988 POPUPMENU *menu = MENU_GetMenu( hmenu );
1990 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
1992 if (menu && top_popup)
1994 HMENU hsubmenu;
1995 POPUPMENU *submenu;
1996 MENUITEM *item;
1998 if (menu->FocusedItem != NO_SELECTED_ITEM)
2000 item = &menu->items[menu->FocusedItem];
2001 if (!(item->fType & MF_POPUP) ||
2002 !(item->fState & MF_MOUSESELECT)) return;
2003 item->fState &= ~MF_MOUSESELECT;
2004 hsubmenu = item->hSubMenu;
2005 } else return;
2007 submenu = MENU_GetMenu( hsubmenu );
2008 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2009 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2010 DestroyWindow( submenu->hWnd );
2011 submenu->hWnd = 0;
2016 /***********************************************************************
2017 * MENU_ShowSubPopup
2019 * Display the sub-menu of the selected item of this menu.
2020 * Return the handle of the submenu, or hmenu if no submenu to display.
2022 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2023 BOOL selectFirst, UINT wFlags )
2025 RECT rect;
2026 POPUPMENU *menu;
2027 MENUITEM *item;
2028 HDC hdc;
2030 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2032 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2034 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2036 item = &menu->items[menu->FocusedItem];
2037 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2038 return hmenu;
2040 /* message must be sent before using item,
2041 because nearly everything may be changed by the application ! */
2043 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2044 if (!(wFlags & TPM_NONOTIFY))
2045 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2046 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2048 item = &menu->items[menu->FocusedItem];
2049 rect = item->rect;
2051 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2052 if (!(item->fState & MF_HILITE))
2054 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2055 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2057 SelectObject( hdc, hMenuFont);
2059 item->fState |= MF_HILITE;
2060 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2061 ReleaseDC( menu->hWnd, hdc );
2063 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2064 item->rect = rect;
2066 item->fState |= MF_MOUSESELECT;
2068 if (IS_SYSTEM_MENU(menu))
2070 MENU_InitSysMenuPopup(item->hSubMenu,
2071 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2072 GetClassLongW( menu->hWnd, GCL_STYLE));
2074 NC_GetSysPopupPos( menu->hWnd, &rect );
2075 rect.top = rect.bottom;
2076 rect.right = GetSystemMetrics(SM_CXSIZE);
2077 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2079 else
2081 GetWindowRect( menu->hWnd, &rect );
2082 if (menu->wFlags & MF_POPUP)
2084 rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
2085 rect.top += item->rect.top;
2086 rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
2087 rect.bottom = item->rect.top - item->rect.bottom;
2089 else
2091 rect.left += item->rect.left;
2092 rect.top += item->rect.bottom;
2093 rect.right = item->rect.right - item->rect.left;
2094 rect.bottom = item->rect.bottom - item->rect.top;
2098 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2099 rect.left, rect.top, rect.right, rect.bottom );
2100 if (selectFirst)
2101 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2102 return item->hSubMenu;
2107 /**********************************************************************
2108 * MENU_IsMenuActive
2110 BOOL MENU_IsMenuActive(void)
2112 return (top_popup != 0);
2115 /***********************************************************************
2116 * MENU_PtMenu
2118 * Walks menu chain trying to find a menu pt maps to.
2120 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2122 POPUPMENU *menu = MENU_GetMenu( hMenu );
2123 UINT item = menu->FocusedItem;
2124 HMENU ret;
2126 /* try subpopup first (if any) */
2127 ret = (item != NO_SELECTED_ITEM &&
2128 (menu->items[item].fType & MF_POPUP) &&
2129 (menu->items[item].fState & MF_MOUSESELECT))
2130 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2132 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2134 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2135 if( menu->wFlags & MF_POPUP )
2137 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2139 else if (ht == HTSYSMENU)
2140 ret = get_win_sys_menu( menu->hWnd );
2141 else if (ht == HTMENU)
2142 ret = GetMenu( menu->hWnd );
2144 return ret;
2147 /***********************************************************************
2148 * MENU_ExecFocusedItem
2150 * Execute a menu item (for instance when user pressed Enter).
2151 * Return the wID of the executed item. Otherwise, -1 indicating
2152 * that no menu item was executed;
2153 * Have to receive the flags for the TrackPopupMenu options to avoid
2154 * sending unwanted message.
2157 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2159 MENUITEM *item;
2160 POPUPMENU *menu = MENU_GetMenu( hMenu );
2162 TRACE("%p hmenu=%p\n", pmt, hMenu);
2164 if (!menu || !menu->nItems ||
2165 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2167 item = &menu->items[menu->FocusedItem];
2169 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2171 if (!(item->fType & MF_POPUP))
2173 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2175 /* If TPM_RETURNCMD is set you return the id, but
2176 do not send a message to the owner */
2177 if(!(wFlags & TPM_RETURNCMD))
2179 if( menu->wFlags & MF_SYSMENU )
2180 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2181 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2182 else
2183 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2185 return item->wID;
2188 else
2189 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2191 return -1;
2194 /***********************************************************************
2195 * MENU_SwitchTracking
2197 * Helper function for menu navigation routines.
2199 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2201 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2202 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2204 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2206 if( pmt->hTopMenu != hPtMenu &&
2207 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2209 /* both are top level menus (system and menu-bar) */
2210 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2211 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2212 pmt->hTopMenu = hPtMenu;
2214 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2215 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2219 /***********************************************************************
2220 * MENU_ButtonDown
2222 * Return TRUE if we can go on with menu tracking.
2224 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2226 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2228 if (hPtMenu)
2230 UINT id = 0;
2231 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2232 MENUITEM *item;
2234 if( IS_SYSTEM_MENU(ptmenu) )
2235 item = ptmenu->items;
2236 else
2237 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2239 if( item )
2241 if( ptmenu->FocusedItem != id )
2242 MENU_SwitchTracking( pmt, hPtMenu, id );
2244 /* If the popup menu is not already "popped" */
2245 if(!(item->fState & MF_MOUSESELECT ))
2247 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2249 /* In win31, a newly popped menu always remains opened for the next buttonup */
2250 if(TWEAK_WineLook == WIN31_LOOK)
2251 ptmenu->bTimeToHide = FALSE;
2254 return TRUE;
2256 /* Else the click was on the menu bar, finish the tracking */
2258 return FALSE;
2261 /***********************************************************************
2262 * MENU_ButtonUp
2264 * Return the value of MENU_ExecFocusedItem if
2265 * the selected item was not a popup. Else open the popup.
2266 * A -1 return value indicates that we go on with menu tracking.
2269 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2271 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2273 if (hPtMenu)
2275 UINT id = 0;
2276 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2277 MENUITEM *item;
2279 if( IS_SYSTEM_MENU(ptmenu) )
2280 item = ptmenu->items;
2281 else
2282 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2284 if( item && (ptmenu->FocusedItem == id ))
2286 if( !(item->fType & MF_POPUP) )
2287 return MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2289 /* If we are dealing with the top-level menu */
2290 /* and this is a click on an already "popped" item: */
2291 /* Stop the menu tracking and close the opened submenus */
2292 if((pmt->hTopMenu == hPtMenu) && (ptmenu->bTimeToHide == TRUE))
2293 return 0;
2295 ptmenu->bTimeToHide = TRUE;
2297 return -1;
2301 /***********************************************************************
2302 * MENU_MouseMove
2304 * Return TRUE if we can go on with menu tracking.
2306 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2308 UINT id = NO_SELECTED_ITEM;
2309 POPUPMENU *ptmenu = NULL;
2311 if( hPtMenu )
2313 ptmenu = MENU_GetMenu( hPtMenu );
2314 if( IS_SYSTEM_MENU(ptmenu) )
2315 id = 0;
2316 else
2317 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2320 if( id == NO_SELECTED_ITEM )
2322 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2323 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2326 else if( ptmenu->FocusedItem != id )
2328 MENU_SwitchTracking( pmt, hPtMenu, id );
2329 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2331 return TRUE;
2335 /***********************************************************************
2336 * MENU_SetCapture
2338 static void MENU_SetCapture( HWND hwnd )
2340 HWND previous = 0;
2342 SERVER_START_REQ( set_capture_window )
2344 req->handle = hwnd;
2345 req->flags = CAPTURE_MENU;
2346 if (!wine_server_call_err( req ))
2348 previous = reply->previous;
2349 hwnd = reply->full_handle;
2352 SERVER_END_REQ;
2354 if (previous && previous != hwnd)
2355 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2359 /***********************************************************************
2360 * MENU_DoNextMenu
2362 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2364 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2366 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2368 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2369 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2371 MDINEXTMENU next_menu;
2372 HMENU hNewMenu;
2373 HWND hNewWnd;
2374 UINT id = 0;
2376 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2377 next_menu.hmenuNext = 0;
2378 next_menu.hwndNext = 0;
2379 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2381 TRACE("%p [%p] -> %p [%p]\n",
2382 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2384 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2386 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2387 hNewWnd = pmt->hOwnerWnd;
2388 if( IS_SYSTEM_MENU(menu) )
2390 /* switch to the menu bar */
2392 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2394 if( vk == VK_LEFT )
2396 menu = MENU_GetMenu( hNewMenu );
2397 id = menu->nItems - 1;
2400 else if (style & WS_SYSMENU )
2402 /* switch to the system menu */
2403 hNewMenu = get_win_sys_menu( hNewWnd );
2405 else return FALSE;
2407 else /* application returned a new menu to switch to */
2409 hNewMenu = next_menu.hmenuNext;
2410 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2412 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2414 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2416 if (style & WS_SYSMENU &&
2417 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2419 /* get the real system menu */
2420 hNewMenu = get_win_sys_menu(hNewWnd);
2422 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2424 /* FIXME: Not sure what to do here;
2425 * perhaps try to track hNewMenu as a popup? */
2427 TRACE(" -- got confused.\n");
2428 return FALSE;
2431 else return FALSE;
2434 if( hNewMenu != pmt->hTopMenu )
2436 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2437 FALSE, 0 );
2438 if( pmt->hCurrentMenu != pmt->hTopMenu )
2439 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2442 if( hNewWnd != pmt->hOwnerWnd )
2444 pmt->hOwnerWnd = hNewWnd;
2445 MENU_SetCapture( pmt->hOwnerWnd );
2448 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2449 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2451 return TRUE;
2453 return FALSE;
2456 /***********************************************************************
2457 * MENU_SuspendPopup
2459 * The idea is not to show the popup if the next input message is
2460 * going to hide it anyway.
2462 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2464 MSG msg;
2466 msg.hwnd = pmt->hOwnerWnd;
2468 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2469 pmt->trackFlags |= TF_SKIPREMOVE;
2471 switch( uMsg )
2473 case WM_KEYDOWN:
2474 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2475 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2477 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2478 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2479 if( msg.message == WM_KEYDOWN &&
2480 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2482 pmt->trackFlags |= TF_SUSPENDPOPUP;
2483 return TRUE;
2486 break;
2489 /* failures go through this */
2490 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2491 return FALSE;
2494 /***********************************************************************
2495 * MENU_KeyEscape
2497 * Handle a VK_ESCAPE key event in a menu.
2499 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2501 BOOL bEndMenu = TRUE;
2503 if (pmt->hCurrentMenu != pmt->hTopMenu)
2505 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2507 if (menu->wFlags & MF_POPUP)
2509 HMENU hmenutmp, hmenuprev;
2511 hmenuprev = hmenutmp = pmt->hTopMenu;
2513 /* close topmost popup */
2514 while (hmenutmp != pmt->hCurrentMenu)
2516 hmenuprev = hmenutmp;
2517 hmenutmp = MENU_GetSubPopup( hmenuprev );
2520 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2521 pmt->hCurrentMenu = hmenuprev;
2522 bEndMenu = FALSE;
2526 return bEndMenu;
2529 /***********************************************************************
2530 * MENU_KeyLeft
2532 * Handle a VK_LEFT key event in a menu.
2534 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2536 POPUPMENU *menu;
2537 HMENU hmenutmp, hmenuprev;
2538 UINT prevcol;
2540 hmenuprev = hmenutmp = pmt->hTopMenu;
2541 menu = MENU_GetMenu( hmenutmp );
2543 /* Try to move 1 column left (if possible) */
2544 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2545 NO_SELECTED_ITEM ) {
2547 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2548 prevcol, TRUE, 0 );
2549 return;
2552 /* close topmost popup */
2553 while (hmenutmp != pmt->hCurrentMenu)
2555 hmenuprev = hmenutmp;
2556 hmenutmp = MENU_GetSubPopup( hmenuprev );
2559 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2560 pmt->hCurrentMenu = hmenuprev;
2562 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2564 /* move menu bar selection if no more popups are left */
2566 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2567 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2569 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2571 /* A sublevel menu was displayed - display the next one
2572 * unless there is another displacement coming up */
2574 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2575 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2576 pmt->hTopMenu, TRUE, wFlags);
2582 /***********************************************************************
2583 * MENU_KeyRight
2585 * Handle a VK_RIGHT key event in a menu.
2587 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2589 HMENU hmenutmp;
2590 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2591 UINT nextcol;
2593 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2594 pmt->hCurrentMenu,
2595 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2596 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2598 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2600 /* If already displaying a popup, try to display sub-popup */
2602 hmenutmp = pmt->hCurrentMenu;
2603 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2605 /* if subpopup was displayed then we are done */
2606 if (hmenutmp != pmt->hCurrentMenu) return;
2609 /* Check to see if there's another column */
2610 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2611 NO_SELECTED_ITEM ) {
2612 TRACE("Going to %d.\n", nextcol );
2613 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2614 nextcol, TRUE, 0 );
2615 return;
2618 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2620 if( pmt->hCurrentMenu != pmt->hTopMenu )
2622 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2623 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2624 } else hmenutmp = 0;
2626 /* try to move to the next item */
2627 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2628 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2630 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2631 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2632 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2633 pmt->hTopMenu, TRUE, wFlags);
2637 /***********************************************************************
2638 * MENU_TrackMenu
2640 * Menu tracking code.
2642 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2643 HWND hwnd, const RECT *lprect )
2645 MSG msg;
2646 POPUPMENU *menu;
2647 BOOL fRemove;
2648 INT executedMenuId = -1;
2649 MTRACKER mt;
2650 BOOL enterIdleSent = FALSE;
2652 mt.trackFlags = 0;
2653 mt.hCurrentMenu = hmenu;
2654 mt.hTopMenu = hmenu;
2655 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2656 mt.pt.x = x;
2657 mt.pt.y = y;
2659 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2660 hmenu, wFlags, x, y, hwnd, (lprect) ? lprect->left : 0, (lprect) ? lprect->top : 0,
2661 (lprect) ? lprect->right : 0, (lprect) ? lprect->bottom : 0);
2663 fEndMenu = FALSE;
2664 if (!(menu = MENU_GetMenu( hmenu )))
2666 SetLastError(ERROR_INVALID_MENU_HANDLE);
2667 return FALSE;
2670 if (wFlags & TPM_BUTTONDOWN)
2672 /* Get the result in order to start the tracking or not */
2673 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2674 fEndMenu = !fRemove;
2677 MENU_SetCapture( mt.hOwnerWnd );
2679 while (!fEndMenu)
2681 menu = MENU_GetMenu( mt.hCurrentMenu );
2682 if (!menu) /* sometimes happens if I do a window manager close */
2683 break;
2685 /* we have to keep the message in the queue until it's
2686 * clear that menu loop is not over yet. */
2688 for (;;)
2690 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2692 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2693 /* remove the message from the queue */
2694 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2696 else
2698 if (!enterIdleSent)
2700 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2701 enterIdleSent = TRUE;
2702 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2704 WaitMessage();
2708 /* check if EndMenu() tried to cancel us, by posting this message */
2709 if(msg.message == WM_CANCELMODE)
2711 /* we are now out of the loop */
2712 fEndMenu = TRUE;
2714 /* remove the message from the queue */
2715 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2717 /* break out of internal loop, ala ESCAPE */
2718 break;
2721 TranslateMessage( &msg );
2722 mt.pt = msg.pt;
2724 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
2725 enterIdleSent=FALSE;
2727 fRemove = FALSE;
2728 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
2731 * Use the mouse coordinates in lParam instead of those in the MSG
2732 * struct to properly handle synthetic messages. They are already
2733 * in screen coordinates.
2735 mt.pt.x = (short)LOWORD(msg.lParam);
2736 mt.pt.y = (short)HIWORD(msg.lParam);
2738 /* Find a menu for this mouse event */
2739 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
2741 switch(msg.message)
2743 /* no WM_NC... messages in captured state */
2745 case WM_RBUTTONDBLCLK:
2746 case WM_RBUTTONDOWN:
2747 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2748 /* fall through */
2749 case WM_LBUTTONDBLCLK:
2750 case WM_LBUTTONDOWN:
2751 /* If the message belongs to the menu, removes it from the queue */
2752 /* Else, end menu tracking */
2753 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2754 fEndMenu = !fRemove;
2755 break;
2757 case WM_RBUTTONUP:
2758 if (!(wFlags & TPM_RIGHTBUTTON)) break;
2759 /* fall through */
2760 case WM_LBUTTONUP:
2761 /* Check if a menu was selected by the mouse */
2762 if (hmenu)
2764 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
2766 /* End the loop if executedMenuId is an item ID */
2767 /* or if the job was done (executedMenuId = 0). */
2768 fEndMenu = fRemove = (executedMenuId != -1);
2770 /* No menu was selected by the mouse */
2771 /* if the function was called by TrackPopupMenu, continue
2772 with the menu tracking. If not, stop it */
2773 else
2774 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
2776 break;
2778 case WM_MOUSEMOVE:
2779 /* In win95 winelook, the selected menu item must be changed every time the
2780 mouse moves. In Win31 winelook, the mouse button has to be held down */
2782 if ( hmenu && ((TWEAK_WineLook > WIN31_LOOK) ||
2783 ( (msg.wParam & MK_LBUTTON) ||
2784 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))) )
2786 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
2788 } /* switch(msg.message) - mouse */
2790 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
2792 fRemove = TRUE; /* Keyboard messages are always removed */
2793 switch(msg.message)
2795 case WM_KEYDOWN:
2796 switch(msg.wParam)
2798 case VK_HOME:
2799 case VK_END:
2800 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
2801 NO_SELECTED_ITEM, FALSE, 0 );
2802 /* fall through */
2803 case VK_UP:
2804 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
2805 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
2806 break;
2808 case VK_DOWN: /* If on menu bar, pull-down the menu */
2810 menu = MENU_GetMenu( mt.hCurrentMenu );
2811 if (!(menu->wFlags & MF_POPUP))
2812 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
2813 else /* otherwise try to move selection */
2814 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT );
2815 break;
2817 case VK_LEFT:
2818 MENU_KeyLeft( &mt, wFlags );
2819 break;
2821 case VK_RIGHT:
2822 MENU_KeyRight( &mt, wFlags );
2823 break;
2825 case VK_ESCAPE:
2826 fEndMenu = MENU_KeyEscape(&mt, wFlags);
2827 break;
2829 case VK_F1:
2831 HELPINFO hi;
2832 hi.cbSize = sizeof(HELPINFO);
2833 hi.iContextType = HELPINFO_MENUITEM;
2834 if (menu->FocusedItem == NO_SELECTED_ITEM)
2835 hi.iCtrlId = 0;
2836 else
2837 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
2838 hi.hItemHandle = hmenu;
2839 hi.dwContextId = menu->dwContextHelpID;
2840 hi.MousePos = msg.pt;
2841 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
2842 break;
2845 default:
2846 break;
2848 break; /* WM_KEYDOWN */
2850 case WM_SYSKEYDOWN:
2851 switch(msg.wParam)
2853 case VK_MENU:
2854 fEndMenu = TRUE;
2855 break;
2858 break; /* WM_SYSKEYDOWN */
2860 case WM_CHAR:
2862 UINT pos;
2864 if (msg.wParam == '\r' || msg.wParam == ' ')
2866 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2867 fEndMenu = (executedMenuId != -1);
2869 break;
2872 /* Hack to avoid control chars. */
2873 /* We will find a better way real soon... */
2874 if (msg.wParam < 32) break;
2876 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
2877 LOWORD(msg.wParam), FALSE );
2878 if (pos == (UINT)-2) fEndMenu = TRUE;
2879 else if (pos == (UINT)-1) MessageBeep(0);
2880 else
2882 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
2883 TRUE, 0 );
2884 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
2885 fEndMenu = (executedMenuId != -1);
2888 break;
2889 } /* switch(msg.message) - kbd */
2891 else
2893 DispatchMessageW( &msg );
2896 if (!fEndMenu) fRemove = TRUE;
2898 /* finally remove message from the queue */
2900 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
2901 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2902 else mt.trackFlags &= ~TF_SKIPREMOVE;
2905 MENU_SetCapture(0); /* release the capture */
2907 /* If dropdown is still painted and the close box is clicked on
2908 then the menu will be destroyed as part of the DispatchMessage above.
2909 This will then invalidate the menu handle in mt.hTopMenu. We should
2910 check for this first. */
2911 if( IsMenu( mt.hTopMenu ) )
2913 menu = MENU_GetMenu( mt.hTopMenu );
2915 if( IsWindow( mt.hOwnerWnd ) )
2917 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
2919 if (menu && menu->wFlags & MF_POPUP)
2921 DestroyWindow( menu->hWnd );
2922 menu->hWnd = 0;
2924 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2925 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
2928 /* Reset the variable for hiding menu */
2929 if( menu ) menu->bTimeToHide = FALSE;
2932 /* The return value is only used by TrackPopupMenu */
2933 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
2934 if (executedMenuId == -1) executedMenuId = 0;
2935 return executedMenuId;
2938 /***********************************************************************
2939 * MENU_InitTracking
2941 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
2943 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
2945 HideCaret(0);
2947 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2948 if (!(wFlags & TPM_NONOTIFY))
2949 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
2951 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
2953 if (!(wFlags & TPM_NONOTIFY))
2955 POPUPMENU *menu;
2956 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
2957 if ((menu = MENU_GetMenu( hMenu )) && (!menu->Height))
2958 { /* app changed/recreated menu bar entries in WM_INITMENU
2959 Recalculate menu sizes else clicks will not work */
2960 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
2961 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2965 return TRUE;
2967 /***********************************************************************
2968 * MENU_ExitTracking
2970 static BOOL MENU_ExitTracking(HWND hWnd)
2972 TRACE("hwnd=%p\n", hWnd);
2974 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
2975 ShowCaret(0);
2976 return TRUE;
2979 /***********************************************************************
2980 * MENU_TrackMouseMenuBar
2982 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2984 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
2986 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
2987 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
2989 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
2991 if (IsMenu(hMenu))
2993 /* map point to parent client coordinates */
2994 HWND parent = GetAncestor( hWnd, GA_PARENT );
2995 if (parent != GetDesktopWindow()) ScreenToClient( parent, &pt );
2997 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
2998 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
2999 MENU_ExitTracking(hWnd);
3004 /***********************************************************************
3005 * MENU_TrackKbdMenuBar
3007 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3009 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3011 UINT uItem = NO_SELECTED_ITEM;
3012 HMENU hTrackMenu;
3013 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3015 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3017 /* find window that has a menu */
3019 while (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)
3020 if (!(hwnd = GetParent( hwnd ))) return;
3022 /* check if we have to track a system menu */
3024 hTrackMenu = GetMenu( hwnd );
3025 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3027 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3028 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_MANAGED) return;
3029 hTrackMenu = get_win_sys_menu( hwnd );
3030 uItem = 0;
3031 wParam |= HTSYSMENU; /* prevent item lookup */
3034 if (!IsMenu( hTrackMenu )) return;
3036 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3038 if( wChar && wChar != ' ' )
3040 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3041 if( uItem >= (UINT)(-2) )
3043 if( uItem == (UINT)(-1) ) MessageBeep(0);
3044 hTrackMenu = 0;
3048 if( hTrackMenu )
3050 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3052 if( uItem == NO_SELECTED_ITEM )
3053 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3054 else if( wChar )
3055 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3057 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3059 MENU_ExitTracking( hwnd );
3063 /**********************************************************************
3064 * TrackPopupMenu (USER32.@)
3066 * Like the win32 API, the function return the command ID only if the
3067 * flag TPM_RETURNCMD is on.
3070 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3071 INT nReserved, HWND hWnd, const RECT *lpRect )
3073 BOOL ret = FALSE;
3075 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3077 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3078 if (!(wFlags & TPM_NONOTIFY))
3079 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3081 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3082 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3083 MENU_ExitTracking(hWnd);
3085 return ret;
3088 /**********************************************************************
3089 * TrackPopupMenuEx (USER32.@)
3091 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3092 HWND hWnd, LPTPMPARAMS lpTpm )
3094 FIXME("not fully implemented\n" );
3095 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3096 lpTpm ? &lpTpm->rcExclude : NULL );
3099 /***********************************************************************
3100 * PopupMenuWndProc
3102 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3104 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3106 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3108 switch(message)
3110 case WM_CREATE:
3112 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3113 SetWindowLongW( hwnd, 0, (LONG)cs->lpCreateParams );
3114 return 0;
3117 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3118 return MA_NOACTIVATE;
3120 case WM_PAINT:
3122 PAINTSTRUCT ps;
3123 BeginPaint( hwnd, &ps );
3124 MENU_DrawPopupMenu( hwnd, ps.hdc,
3125 (HMENU)GetWindowLongW( hwnd, 0 ) );
3126 EndPaint( hwnd, &ps );
3127 return 0;
3129 case WM_ERASEBKGND:
3130 return 1;
3132 case WM_DESTROY:
3133 /* zero out global pointer in case resident popup window was destroyed. */
3134 if (hwnd == top_popup) top_popup = 0;
3135 break;
3137 case WM_SHOWWINDOW:
3139 if( wParam )
3141 if (!GetWindowLongW( hwnd, 0 )) ERR("no menu to display\n");
3143 else
3144 SetWindowLongW( hwnd, 0, 0 );
3145 break;
3147 case MM_SETMENUHANDLE:
3148 SetWindowLongW( hwnd, 0, wParam );
3149 break;
3151 case MM_GETMENUHANDLE:
3152 return GetWindowLongW( hwnd, 0 );
3154 default:
3155 return DefWindowProcW( hwnd, message, wParam, lParam );
3157 return 0;
3161 /***********************************************************************
3162 * MENU_GetMenuBarHeight
3164 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3166 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3167 INT orgX, INT orgY )
3169 HDC hdc;
3170 RECT rectBar;
3171 LPPOPUPMENU lppop;
3173 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3175 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3177 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3178 SelectObject( hdc, hMenuFont);
3179 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3180 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3181 ReleaseDC( hwnd, hdc );
3182 return lppop->Height;
3186 /*******************************************************************
3187 * ChangeMenuA (USER32.@)
3189 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3190 UINT id, UINT flags )
3192 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3193 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3194 id, data );
3195 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3196 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3197 id, data );
3198 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3199 flags & MF_BYPOSITION ? pos : id,
3200 flags & ~MF_REMOVE );
3201 /* Default: MF_INSERT */
3202 return InsertMenuA( hMenu, pos, flags, id, data );
3206 /*******************************************************************
3207 * ChangeMenuW (USER32.@)
3209 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3210 UINT id, UINT flags )
3212 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3213 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3214 id, data );
3215 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3216 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3217 id, data );
3218 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3219 flags & MF_BYPOSITION ? pos : id,
3220 flags & ~MF_REMOVE );
3221 /* Default: MF_INSERT */
3222 return InsertMenuW( hMenu, pos, flags, id, data );
3226 /*******************************************************************
3227 * CheckMenuItem (USER32.@)
3229 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3231 MENUITEM *item;
3232 DWORD ret;
3234 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3235 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3236 ret = item->fState & MF_CHECKED;
3237 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3238 else item->fState &= ~MF_CHECKED;
3239 return ret;
3243 /**********************************************************************
3244 * EnableMenuItem (USER32.@)
3246 UINT WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3248 UINT oldflags;
3249 MENUITEM *item;
3250 POPUPMENU *menu;
3252 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3254 /* Get the Popupmenu to access the owner menu */
3255 if (!(menu = MENU_GetMenu(hMenu)))
3256 return (UINT)-1;
3258 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3259 return (UINT)-1;
3261 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3262 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3264 /* In win95 if the close item in the system menu change update the close button */
3265 if (TWEAK_WineLook == WIN95_LOOK)
3266 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3268 if (menu->hSysMenuOwner != 0)
3270 POPUPMENU* parentMenu;
3272 /* Get the parent menu to access*/
3273 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3274 return (UINT)-1;
3276 /* Refresh the frame to reflect the change*/
3277 SetWindowPos(parentMenu->hWnd, 0, 0, 0, 0, 0,
3278 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
3282 return oldflags;
3286 /*******************************************************************
3287 * GetMenuStringA (USER32.@)
3289 INT WINAPI GetMenuStringA(
3290 HMENU hMenu, /* [in] menuhandle */
3291 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3292 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3293 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3294 UINT wFlags /* [in] MF_ flags */
3296 MENUITEM *item;
3298 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3299 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3300 if (!IS_STRING_ITEM(item->fType)) return 0;
3301 if (!str || !nMaxSiz) return strlenW(item->text);
3302 str[0] = '\0';
3303 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3304 str[nMaxSiz-1] = 0;
3305 TRACE("returning '%s'\n", str );
3306 return strlen(str);
3310 /*******************************************************************
3311 * GetMenuStringW (USER32.@)
3313 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3314 LPWSTR str, INT nMaxSiz, UINT wFlags )
3316 MENUITEM *item;
3318 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3319 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return 0;
3320 if (!IS_STRING_ITEM(item->fType)) return 0;
3321 if (!str || !nMaxSiz) return strlenW(item->text);
3322 str[0] = '\0';
3323 lstrcpynW( str, item->text, nMaxSiz );
3324 return strlenW(str);
3328 /**********************************************************************
3329 * HiliteMenuItem (USER32.@)
3331 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3332 UINT wHilite )
3334 LPPOPUPMENU menu;
3335 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3336 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3337 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3338 if (menu->FocusedItem == wItemID) return TRUE;
3339 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3340 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3341 return TRUE;
3345 /**********************************************************************
3346 * GetMenuState (USER32.@)
3348 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3350 MENUITEM *item;
3351 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3352 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3353 debug_print_menuitem (" item: ", item, "");
3354 if (item->fType & MF_POPUP)
3356 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3357 if (!menu) return -1;
3358 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3360 else
3362 /* We used to (from way back then) mask the result to 0xff. */
3363 /* I don't know why and it seems wrong as the documented */
3364 /* return flag MF_SEPARATOR is outside that mask. */
3365 return (item->fType | item->fState);
3370 /**********************************************************************
3371 * GetMenuItemCount (USER32.@)
3373 INT WINAPI GetMenuItemCount( HMENU hMenu )
3375 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3376 if (!menu) return -1;
3377 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3378 return menu->nItems;
3382 /**********************************************************************
3383 * GetMenuItemID (USER32.@)
3385 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3387 MENUITEM * lpmi;
3389 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return -1;
3390 if (lpmi->fType & MF_POPUP) return -1;
3391 return lpmi->wID;
3396 /*******************************************************************
3397 * InsertMenuW (USER32.@)
3399 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3400 UINT_PTR id, LPCWSTR str )
3402 MENUITEM *item;
3404 if (IS_STRING_ITEM(flags) && str)
3405 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3406 hMenu, pos, flags, id, debugstr_w(str) );
3407 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3408 hMenu, pos, flags, id, (DWORD)str );
3410 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3412 if (!(MENU_SetItemData( item, flags, id, str )))
3414 RemoveMenu( hMenu, pos, flags );
3415 return FALSE;
3418 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3419 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3421 item->hCheckBit = item->hUnCheckBit = 0;
3422 return TRUE;
3426 /*******************************************************************
3427 * InsertMenuA (USER32.@)
3429 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3430 UINT_PTR id, LPCSTR str )
3432 BOOL ret = FALSE;
3434 if (IS_STRING_ITEM(flags) && str)
3436 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3437 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3438 if (newstr)
3440 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3441 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3442 HeapFree( GetProcessHeap(), 0, newstr );
3444 return ret;
3446 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3450 /*******************************************************************
3451 * AppendMenuA (USER32.@)
3453 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3454 UINT_PTR id, LPCSTR data )
3456 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3460 /*******************************************************************
3461 * AppendMenuW (USER32.@)
3463 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3464 UINT_PTR id, LPCWSTR data )
3466 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3470 /**********************************************************************
3471 * RemoveMenu (USER32.@)
3473 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3475 LPPOPUPMENU menu;
3476 MENUITEM *item;
3478 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3479 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3480 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3482 /* Remove item */
3484 MENU_FreeItemData( item );
3486 if (--menu->nItems == 0)
3488 HeapFree( GetProcessHeap(), 0, menu->items );
3489 menu->items = NULL;
3491 else
3493 while(nPos < menu->nItems)
3495 *item = *(item+1);
3496 item++;
3497 nPos++;
3499 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3500 menu->nItems * sizeof(MENUITEM) );
3502 return TRUE;
3506 /**********************************************************************
3507 * DeleteMenu (USER32.@)
3509 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3511 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3512 if (!item) return FALSE;
3513 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3514 /* nPos is now the position of the item */
3515 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3516 return TRUE;
3520 /*******************************************************************
3521 * ModifyMenuW (USER32.@)
3523 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3524 UINT_PTR id, LPCWSTR str )
3526 MENUITEM *item;
3528 if (IS_STRING_ITEM(flags))
3530 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3532 else
3534 TRACE("%p %d %04x %04x %08lx\n", hMenu, pos, flags, id, (DWORD)str );
3537 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3538 return MENU_SetItemData( item, flags, id, str );
3542 /*******************************************************************
3543 * ModifyMenuA (USER32.@)
3545 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3546 UINT_PTR id, LPCSTR str )
3548 BOOL ret = FALSE;
3550 if (IS_STRING_ITEM(flags) && str)
3552 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3553 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3554 if (newstr)
3556 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3557 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3558 HeapFree( GetProcessHeap(), 0, newstr );
3560 return ret;
3562 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3566 /**********************************************************************
3567 * CreatePopupMenu (USER32.@)
3569 HMENU WINAPI CreatePopupMenu(void)
3571 HMENU hmenu;
3572 POPUPMENU *menu;
3574 if (!(hmenu = CreateMenu())) return 0;
3575 menu = MENU_GetMenu( hmenu );
3576 menu->wFlags |= MF_POPUP;
3577 menu->bTimeToHide = FALSE;
3578 return hmenu;
3582 /**********************************************************************
3583 * GetMenuCheckMarkDimensions (USER.417)
3584 * GetMenuCheckMarkDimensions (USER32.@)
3586 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3588 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3592 /**********************************************************************
3593 * SetMenuItemBitmaps (USER32.@)
3595 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3596 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3598 MENUITEM *item;
3599 TRACE("(%p, %04x, %04x, %p, %p)\n",
3600 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3601 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3603 if (!hNewCheck && !hNewUnCheck)
3605 item->fState &= ~MF_USECHECKBITMAPS;
3607 else /* Install new bitmaps */
3609 item->hCheckBit = hNewCheck;
3610 item->hUnCheckBit = hNewUnCheck;
3611 item->fState |= MF_USECHECKBITMAPS;
3613 return TRUE;
3617 /**********************************************************************
3618 * CreateMenu (USER32.@)
3620 HMENU WINAPI CreateMenu(void)
3622 HMENU hMenu;
3623 LPPOPUPMENU menu;
3624 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3625 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3627 ZeroMemory(menu, sizeof(POPUPMENU));
3628 menu->wMagic = MENU_MAGIC;
3629 menu->FocusedItem = NO_SELECTED_ITEM;
3630 menu->bTimeToHide = FALSE;
3632 TRACE("return %p\n", hMenu );
3634 return hMenu;
3638 /**********************************************************************
3639 * DestroyMenu (USER32.@)
3641 BOOL WINAPI DestroyMenu( HMENU hMenu )
3643 TRACE("(%p)\n", hMenu);
3645 /* Silently ignore attempts to destroy default system popup */
3647 if (hMenu && hMenu != MENU_DefSysPopup)
3649 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3651 if (!lppop) return FALSE;
3653 lppop->wMagic = 0; /* Mark it as destroyed */
3655 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
3657 DestroyWindow( lppop->hWnd );
3658 lppop->hWnd = 0;
3661 if (lppop->items) /* recursively destroy submenus */
3663 int i;
3664 MENUITEM *item = lppop->items;
3665 for (i = lppop->nItems; i > 0; i--, item++)
3667 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3668 MENU_FreeItemData( item );
3670 HeapFree( GetProcessHeap(), 0, lppop->items );
3672 USER_HEAP_FREE( hMenu );
3674 return (hMenu != MENU_DefSysPopup);
3678 /**********************************************************************
3679 * GetSystemMenu (USER32.@)
3681 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3683 WND *wndPtr = WIN_FindWndPtr( hWnd );
3684 HMENU retvalue = 0;
3686 if (wndPtr)
3688 if( wndPtr->hSysMenu )
3690 if( bRevert )
3692 DestroyMenu(wndPtr->hSysMenu);
3693 wndPtr->hSysMenu = 0;
3695 else
3697 POPUPMENU *menu = MENU_GetMenu( wndPtr->hSysMenu );
3698 if( menu )
3700 if( menu->nItems > 0 && menu->items[0].hSubMenu == MENU_DefSysPopup )
3701 menu->items[0].hSubMenu = MENU_CopySysPopup();
3703 else
3705 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3706 wndPtr->hSysMenu, hWnd);
3707 wndPtr->hSysMenu = 0;
3712 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3713 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, (HMENU)(-1) );
3715 if( wndPtr->hSysMenu )
3717 POPUPMENU *menu;
3718 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3720 /* Store the dummy sysmenu handle to facilitate the refresh */
3721 /* of the close button if the SC_CLOSE item change */
3722 menu = MENU_GetMenu(retvalue);
3723 if ( menu )
3724 menu->hSysMenuOwner = wndPtr->hSysMenu;
3726 WIN_ReleaseWndPtr(wndPtr);
3728 return bRevert ? 0 : retvalue;
3732 /*******************************************************************
3733 * SetSystemMenu (USER32.@)
3735 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
3737 WND *wndPtr = WIN_FindWndPtr(hwnd);
3739 if (wndPtr)
3741 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
3742 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
3743 WIN_ReleaseWndPtr(wndPtr);
3744 return TRUE;
3746 return FALSE;
3750 /**********************************************************************
3751 * GetMenu (USER32.@)
3753 HMENU WINAPI GetMenu( HWND hWnd )
3755 HMENU retvalue = (HMENU)GetWindowLongW( hWnd, GWL_ID );
3756 TRACE("for %p returning %p\n", hWnd, retvalue);
3757 return retvalue;
3761 /**********************************************************************
3762 * SetMenu (USER32.@)
3764 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
3766 TRACE("(%p, %p);\n", hWnd, hMenu);
3768 if (hMenu && !IsMenu(hMenu))
3770 WARN("hMenu %p is not a menu handle\n", hMenu);
3771 return FALSE;
3773 if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3775 hWnd = WIN_GetFullHandle( hWnd );
3776 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
3778 if (hMenu != 0)
3780 LPPOPUPMENU lpmenu;
3782 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
3784 lpmenu->hWnd = hWnd;
3785 lpmenu->Height = 0; /* Make sure we recalculate the size */
3787 SetWindowLongW( hWnd, GWL_ID, (LONG_PTR)hMenu );
3789 if (IsWindowVisible(hWnd))
3790 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3791 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3792 return TRUE;
3797 /**********************************************************************
3798 * GetSubMenu (USER32.@)
3800 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
3802 MENUITEM * lpmi;
3804 if (!(lpmi = MENU_FindItem(&hMenu,&nPos,MF_BYPOSITION))) return 0;
3805 if (!(lpmi->fType & MF_POPUP)) return 0;
3806 return lpmi->hSubMenu;
3810 /**********************************************************************
3811 * DrawMenuBar (USER32.@)
3813 BOOL WINAPI DrawMenuBar( HWND hWnd )
3815 LPPOPUPMENU lppop;
3816 HMENU hMenu = GetMenu(hWnd);
3818 if (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) return FALSE;
3819 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
3821 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
3822 lppop->hwndOwner = hWnd;
3823 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3824 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3825 return TRUE;
3828 /***********************************************************************
3829 * DrawMenuBarTemp (USER32.@)
3831 * UNDOCUMENTED !!
3833 * called by W98SE desk.cpl Control Panel Applet
3835 * Not 100% sure about the param names, but close.
3837 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
3839 LPPOPUPMENU lppop;
3840 UINT i,retvalue;
3841 HFONT hfontOld = 0;
3843 if (!hMenu)
3844 hMenu = GetMenu(hwnd);
3846 if (!hFont)
3847 hFont = hMenuFont;
3849 lppop = MENU_GetMenu( hMenu );
3850 if (lppop == NULL || lprect == NULL)
3852 retvalue = GetSystemMetrics(SM_CYMENU);
3853 goto END;
3856 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
3858 hfontOld = SelectObject( hDC, hFont);
3860 if (lppop->Height == 0)
3861 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
3863 lprect->bottom = lprect->top + lppop->Height;
3865 FillRect(hDC, lprect, GetSysColorBrush(COLOR_MENU) );
3867 if (TWEAK_WineLook == WIN31_LOOK)
3869 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
3870 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3871 LineTo( hDC, lprect->right, lprect->bottom );
3873 else
3875 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
3876 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
3877 LineTo( hDC, lprect->right, lprect->bottom );
3880 if (lppop->nItems == 0)
3882 retvalue = GetSystemMetrics(SM_CYMENU);
3883 goto END;
3886 for (i = 0; i < lppop->nItems; i++)
3888 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
3889 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
3891 retvalue = lppop->Height;
3893 END:
3894 if (hfontOld) SelectObject (hDC, hfontOld);
3895 return retvalue;
3898 /***********************************************************************
3899 * EndMenu (USER.187)
3900 * EndMenu (USER32.@)
3902 void WINAPI EndMenu(void)
3904 /* if we are in the menu code, and it is active */
3905 if (!fEndMenu && top_popup)
3907 /* terminate the menu handling code */
3908 fEndMenu = TRUE;
3910 /* needs to be posted to wakeup the internal menu handler */
3911 /* which will now terminate the menu, in the event that */
3912 /* the main window was minimized, or lost focus, so we */
3913 /* don't end up with an orphaned menu */
3914 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
3919 /***********************************************************************
3920 * LookupMenuHandle (USER.217)
3922 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
3924 HMENU hmenu32 = HMENU_32(hmenu);
3925 UINT id32 = id;
3926 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
3927 else return HMENU_16(hmenu32);
3931 /**********************************************************************
3932 * LoadMenu (USER.150)
3934 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
3936 HRSRC16 hRsrc;
3937 HGLOBAL16 handle;
3938 HMENU16 hMenu;
3940 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
3941 if (!name) return 0;
3943 instance = GetExePtr( instance );
3944 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
3945 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
3946 hMenu = LoadMenuIndirect16(LockResource16(handle));
3947 FreeResource16( handle );
3948 return hMenu;
3952 /*****************************************************************
3953 * LoadMenuA (USER32.@)
3955 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
3957 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
3958 if (!hrsrc) return 0;
3959 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
3963 /*****************************************************************
3964 * LoadMenuW (USER32.@)
3966 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
3968 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
3969 if (!hrsrc) return 0;
3970 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
3974 /**********************************************************************
3975 * LoadMenuIndirect (USER.220)
3977 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
3979 HMENU hMenu;
3980 WORD version, offset;
3981 LPCSTR p = (LPCSTR)template;
3983 TRACE("(%p)\n", template );
3984 version = GET_WORD(p);
3985 p += sizeof(WORD);
3986 if (version)
3988 WARN("version must be 0 for Win16\n" );
3989 return 0;
3991 offset = GET_WORD(p);
3992 p += sizeof(WORD) + offset;
3993 if (!(hMenu = CreateMenu())) return 0;
3994 if (!MENU_ParseResource( p, hMenu, FALSE ))
3996 DestroyMenu( hMenu );
3997 return 0;
3999 return HMENU_16(hMenu);
4003 /**********************************************************************
4004 * LoadMenuIndirectW (USER32.@)
4006 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4008 HMENU hMenu;
4009 WORD version, offset;
4010 LPCSTR p = (LPCSTR)template;
4012 version = GET_WORD(p);
4013 p += sizeof(WORD);
4014 TRACE("%p, ver %d\n", template, version );
4015 switch (version)
4017 case 0: /* standard format is version of 0 */
4018 offset = GET_WORD(p);
4019 p += sizeof(WORD) + offset;
4020 if (!(hMenu = CreateMenu())) return 0;
4021 if (!MENU_ParseResource( p, hMenu, TRUE ))
4023 DestroyMenu( hMenu );
4024 return 0;
4026 return hMenu;
4027 case 1: /* extended format is version of 1 */
4028 offset = GET_WORD(p);
4029 p += sizeof(WORD) + offset;
4030 if (!(hMenu = CreateMenu())) return 0;
4031 if (!MENUEX_ParseResource( p, hMenu))
4033 DestroyMenu( hMenu );
4034 return 0;
4036 return hMenu;
4037 default:
4038 ERR("version %d not supported.\n", version);
4039 return 0;
4044 /**********************************************************************
4045 * LoadMenuIndirectA (USER32.@)
4047 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4049 return LoadMenuIndirectW( template );
4053 /**********************************************************************
4054 * IsMenu (USER32.@)
4056 BOOL WINAPI IsMenu(HMENU hmenu)
4058 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4059 return menu != NULL;
4062 /**********************************************************************
4063 * GetMenuItemInfo_common
4066 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4067 LPMENUITEMINFOW lpmii, BOOL unicode)
4069 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos? MF_BYPOSITION : 0);
4071 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4073 if (!menu)
4074 return FALSE;
4076 if (lpmii->fMask & MIIM_TYPE) {
4077 lpmii->fType = menu->fType;
4078 switch (MENU_ITEM_TYPE(menu->fType)) {
4079 case MF_STRING:
4080 break; /* will be done below */
4081 case MF_OWNERDRAW:
4082 case MF_BITMAP:
4083 lpmii->dwTypeData = menu->text;
4084 /* fall through */
4085 default:
4086 lpmii->cch = 0;
4090 /* copy the text string */
4091 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)) &&
4092 (MENU_ITEM_TYPE(menu->fType) == MF_STRING) && menu->text)
4094 int len;
4095 if (unicode)
4097 len = strlenW(menu->text);
4098 if(lpmii->dwTypeData && lpmii->cch)
4099 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4101 else
4103 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL, 0, NULL, NULL );
4104 if(lpmii->dwTypeData && lpmii->cch)
4105 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4106 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4107 ((LPSTR)lpmii->dwTypeData)[lpmii->cch-1] = 0;
4109 /* if we've copied a substring we return its length */
4110 if(lpmii->dwTypeData && lpmii->cch)
4112 if (lpmii->cch <= len) lpmii->cch--;
4114 else /* return length of string */
4115 lpmii->cch = len;
4118 if (lpmii->fMask & MIIM_FTYPE)
4119 lpmii->fType = menu->fType;
4121 if (lpmii->fMask & MIIM_BITMAP)
4122 lpmii->hbmpItem = menu->hbmpItem;
4124 if (lpmii->fMask & MIIM_STATE)
4125 lpmii->fState = menu->fState;
4127 if (lpmii->fMask & MIIM_ID)
4128 lpmii->wID = menu->wID;
4130 if (lpmii->fMask & MIIM_SUBMENU)
4131 lpmii->hSubMenu = menu->hSubMenu;
4133 if (lpmii->fMask & MIIM_CHECKMARKS) {
4134 lpmii->hbmpChecked = menu->hCheckBit;
4135 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4137 if (lpmii->fMask & MIIM_DATA)
4138 lpmii->dwItemData = menu->dwItemData;
4140 return TRUE;
4143 /**********************************************************************
4144 * GetMenuItemInfoA (USER32.@)
4146 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4147 LPMENUITEMINFOA lpmii)
4149 return GetMenuItemInfo_common (hmenu, item, bypos,
4150 (LPMENUITEMINFOW)lpmii, FALSE);
4153 /**********************************************************************
4154 * GetMenuItemInfoW (USER32.@)
4156 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4157 LPMENUITEMINFOW lpmii)
4159 return GetMenuItemInfo_common (hmenu, item, bypos,
4160 lpmii, TRUE);
4164 /* set a menu item text from a ASCII or Unicode string */
4165 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4167 if (!text)
4169 menu->text = NULL;
4170 menu->fType |= MF_SEPARATOR;
4172 else if (unicode)
4174 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4175 strcpyW( menu->text, text );
4177 else
4179 LPCSTR str = (LPCSTR)text;
4180 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4181 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4182 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4187 /**********************************************************************
4188 * SetMenuItemInfo_common
4191 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4192 const MENUITEMINFOW *lpmii,
4193 BOOL unicode)
4195 if (!menu) return FALSE;
4197 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu, "");
4199 if (lpmii->fMask & MIIM_TYPE ) {
4200 /* Get rid of old string. */
4201 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4202 HeapFree(GetProcessHeap(), 0, menu->text);
4203 menu->text = NULL;
4206 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4207 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4208 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4210 menu->text = lpmii->dwTypeData;
4212 if (IS_STRING_ITEM(menu->fType))
4213 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4216 if (lpmii->fMask & MIIM_FTYPE ) {
4217 /* free the string when the type is changing */
4218 if ( (!IS_STRING_ITEM(lpmii->fType)) && IS_STRING_ITEM(menu->fType) && menu->text) {
4219 HeapFree(GetProcessHeap(), 0, menu->text);
4220 menu->text = NULL;
4222 menu->fType &= ~MENU_ITEM_TYPE(menu->fType);
4223 menu->fType |= MENU_ITEM_TYPE(lpmii->fType);
4224 if ( IS_STRING_ITEM(menu->fType) && !menu->text )
4225 menu->fType |= MF_SEPARATOR;
4228 if (lpmii->fMask & MIIM_STRING ) {
4229 /* free the string when used */
4230 if (IS_STRING_ITEM(menu->fType) && menu->text) {
4231 HeapFree(GetProcessHeap(), 0, menu->text);
4232 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4236 if (lpmii->fMask & MIIM_STATE)
4238 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4239 menu->fState = lpmii->fState;
4242 if (lpmii->fMask & MIIM_ID)
4243 menu->wID = lpmii->wID;
4245 if (lpmii->fMask & MIIM_SUBMENU) {
4246 menu->hSubMenu = lpmii->hSubMenu;
4247 if (menu->hSubMenu) {
4248 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4249 if (subMenu) {
4250 subMenu->wFlags |= MF_POPUP;
4251 menu->fType |= MF_POPUP;
4253 else
4254 /* FIXME: Return an error ? */
4255 menu->fType &= ~MF_POPUP;
4257 else
4258 menu->fType &= ~MF_POPUP;
4261 if (lpmii->fMask & MIIM_CHECKMARKS)
4263 if (lpmii->fType & MFT_RADIOCHECK)
4264 menu->fType |= MFT_RADIOCHECK;
4266 menu->hCheckBit = lpmii->hbmpChecked;
4267 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4269 if (lpmii->fMask & MIIM_DATA)
4270 menu->dwItemData = lpmii->dwItemData;
4272 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4273 return TRUE;
4276 /**********************************************************************
4277 * SetMenuItemInfoA (USER32.@)
4279 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4280 const MENUITEMINFOA *lpmii)
4282 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4283 (const MENUITEMINFOW *)lpmii, FALSE);
4286 /**********************************************************************
4287 * SetMenuItemInfoW (USER32.@)
4289 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4290 const MENUITEMINFOW *lpmii)
4292 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4293 lpmii, TRUE);
4296 /**********************************************************************
4297 * SetMenuDefaultItem (USER32.@)
4300 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4302 UINT i;
4303 POPUPMENU *menu;
4304 MENUITEM *item;
4306 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4308 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4310 /* reset all default-item flags */
4311 item = menu->items;
4312 for (i = 0; i < menu->nItems; i++, item++)
4314 item->fState &= ~MFS_DEFAULT;
4317 /* no default item */
4318 if ( -1 == uItem)
4320 return TRUE;
4323 item = menu->items;
4324 if ( bypos )
4326 if ( uItem >= menu->nItems ) return FALSE;
4327 item[uItem].fState |= MFS_DEFAULT;
4328 return TRUE;
4330 else
4332 for (i = 0; i < menu->nItems; i++, item++)
4334 if (item->wID == uItem)
4336 item->fState |= MFS_DEFAULT;
4337 return TRUE;
4342 return FALSE;
4345 /**********************************************************************
4346 * GetMenuDefaultItem (USER32.@)
4348 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4350 POPUPMENU *menu;
4351 MENUITEM * item;
4352 UINT i = 0;
4354 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4356 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4358 /* find default item */
4359 item = menu->items;
4361 /* empty menu */
4362 if (! item) return -1;
4364 while ( !( item->fState & MFS_DEFAULT ) )
4366 i++; item++;
4367 if (i >= menu->nItems ) return -1;
4370 /* default: don't return disabled items */
4371 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4373 /* search rekursiv when needed */
4374 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4376 UINT ret;
4377 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4378 if ( -1 != ret ) return ret;
4380 /* when item not found in submenu, return the popup item */
4382 return ( bypos ) ? i : item->wID;
4387 /**********************************************************************
4388 * InsertMenuItemA (USER32.@)
4390 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4391 const MENUITEMINFOA *lpmii)
4393 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4394 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)lpmii, FALSE);
4398 /**********************************************************************
4399 * InsertMenuItemW (USER32.@)
4401 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4402 const MENUITEMINFOW *lpmii)
4404 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4405 return SetMenuItemInfo_common(item, lpmii, TRUE);
4408 /**********************************************************************
4409 * CheckMenuRadioItem (USER32.@)
4412 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4413 UINT first, UINT last, UINT check,
4414 UINT bypos)
4416 MENUITEM *mifirst, *milast, *micheck;
4417 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4419 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4421 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4422 milast = MENU_FindItem (&mlast, &last, bypos);
4423 micheck = MENU_FindItem (&mcheck, &check, bypos);
4425 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4426 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4427 micheck > milast || micheck < mifirst)
4428 return FALSE;
4430 while (mifirst <= milast)
4432 if (mifirst == micheck)
4434 mifirst->fType |= MFT_RADIOCHECK;
4435 mifirst->fState |= MFS_CHECKED;
4436 } else {
4437 mifirst->fType &= ~MFT_RADIOCHECK;
4438 mifirst->fState &= ~MFS_CHECKED;
4440 mifirst++;
4443 return TRUE;
4447 /**********************************************************************
4448 * GetMenuItemRect (USER32.@)
4450 * ATTENTION: Here, the returned values in rect are the screen
4451 * coordinates of the item just like if the menu was
4452 * always on the upper left side of the application.
4455 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4456 LPRECT rect)
4458 POPUPMENU *itemMenu;
4459 MENUITEM *item;
4460 HWND referenceHwnd;
4462 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4464 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4465 referenceHwnd = hwnd;
4467 if(!hwnd)
4469 itemMenu = MENU_GetMenu(hMenu);
4470 if (itemMenu == NULL)
4471 return FALSE;
4473 if(itemMenu->hWnd == 0)
4474 return FALSE;
4475 referenceHwnd = itemMenu->hWnd;
4478 if ((rect == NULL) || (item == NULL))
4479 return FALSE;
4481 *rect = item->rect;
4483 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4485 return TRUE;
4489 /**********************************************************************
4490 * SetMenuInfo (USER32.@)
4492 * FIXME
4493 * MIM_APPLYTOSUBMENUS
4494 * actually use the items to draw the menu
4496 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4498 POPUPMENU *menu;
4500 TRACE("(%p %p)\n", hMenu, lpmi);
4502 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4505 if (lpmi->fMask & MIM_BACKGROUND)
4506 menu->hbrBack = lpmi->hbrBack;
4508 if (lpmi->fMask & MIM_HELPID)
4509 menu->dwContextHelpID = lpmi->dwContextHelpID;
4511 if (lpmi->fMask & MIM_MAXHEIGHT)
4512 menu->cyMax = lpmi->cyMax;
4514 if (lpmi->fMask & MIM_MENUDATA)
4515 menu->dwMenuData = lpmi->dwMenuData;
4517 if (lpmi->fMask & MIM_STYLE)
4518 menu->dwStyle = lpmi->dwStyle;
4520 return TRUE;
4522 return FALSE;
4525 /**********************************************************************
4526 * GetMenuInfo (USER32.@)
4528 * NOTES
4529 * win98/NT5.0
4532 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4533 { POPUPMENU *menu;
4535 TRACE("(%p %p)\n", hMenu, lpmi);
4537 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4540 if (lpmi->fMask & MIM_BACKGROUND)
4541 lpmi->hbrBack = menu->hbrBack;
4543 if (lpmi->fMask & MIM_HELPID)
4544 lpmi->dwContextHelpID = menu->dwContextHelpID;
4546 if (lpmi->fMask & MIM_MAXHEIGHT)
4547 lpmi->cyMax = menu->cyMax;
4549 if (lpmi->fMask & MIM_MENUDATA)
4550 lpmi->dwMenuData = menu->dwMenuData;
4552 if (lpmi->fMask & MIM_STYLE)
4553 lpmi->dwStyle = menu->dwStyle;
4555 return TRUE;
4557 return FALSE;
4561 /**********************************************************************
4562 * SetMenuContextHelpId (USER32.@)
4564 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4566 LPPOPUPMENU menu;
4568 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4570 if ((menu = MENU_GetMenu(hMenu)))
4572 menu->dwContextHelpID = dwContextHelpID;
4573 return TRUE;
4575 return FALSE;
4579 /**********************************************************************
4580 * GetMenuContextHelpId (USER32.@)
4582 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4584 LPPOPUPMENU menu;
4586 TRACE("(%p)\n", hMenu);
4588 if ((menu = MENU_GetMenu(hMenu)))
4590 return menu->dwContextHelpID;
4592 return 0;
4595 /**********************************************************************
4596 * MenuItemFromPoint (USER32.@)
4598 UINT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4600 POPUPMENU *menu = MENU_GetMenu(hMenu);
4601 UINT pos;
4602 MENUITEM *item;
4604 /*FIXME: Do we have to handle hWnd here? */
4605 item = MENU_FindItemByCoords(menu, ptScreen, &pos);
4607 return pos;
4611 /**********************************************************************
4612 * translate_accelerator
4614 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
4615 BYTE fVirt, WORD key, WORD cmd )
4617 UINT mesg = 0;
4619 if (wParam != key) return FALSE;
4621 if (message == WM_CHAR)
4623 if ( !(fVirt & FALT) && !(fVirt & FVIRTKEY) )
4625 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
4626 goto found;
4629 else
4631 if(fVirt & FVIRTKEY)
4633 INT mask = 0;
4634 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
4635 wParam, 0xff & HIWORD(lParam));
4636 if(GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
4637 if(GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
4638 if(GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
4639 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
4640 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
4642 else
4644 if (!(lParam & 0x01000000)) /* no special_key */
4646 if ((fVirt & FALT) && (lParam & 0x20000000))
4647 { /* ^^ ALT pressed */
4648 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
4649 goto found;
4654 return FALSE;
4656 found:
4657 if (message == WM_KEYUP || message == WM_SYSKEYUP)
4658 mesg = 1;
4659 else if (GetCapture())
4660 mesg = 2;
4661 else if (!IsWindowEnabled(hWnd))
4662 mesg = 3;
4663 else
4665 HMENU hMenu, hSubMenu, hSysMenu;
4666 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
4668 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
4669 hSysMenu = get_win_sys_menu( hWnd );
4671 /* find menu item and ask application to initialize it */
4672 /* 1. in the system menu */
4673 hSubMenu = hSysMenu;
4674 nPos = cmd;
4675 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4677 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
4678 if(hSubMenu != hSysMenu)
4680 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
4681 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
4682 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
4684 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
4686 else /* 2. in the window's menu */
4688 hSubMenu = hMenu;
4689 nPos = cmd;
4690 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
4692 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
4693 if(hSubMenu != hMenu)
4695 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
4696 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
4697 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
4699 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
4703 if (uSysStat != (UINT)-1)
4705 if (uSysStat & (MF_DISABLED|MF_GRAYED))
4706 mesg=4;
4707 else
4708 mesg=WM_SYSCOMMAND;
4710 else
4712 if (uStat != (UINT)-1)
4714 if (IsIconic(hWnd))
4715 mesg=5;
4716 else
4718 if (uStat & (MF_DISABLED|MF_GRAYED))
4719 mesg=6;
4720 else
4721 mesg=WM_COMMAND;
4724 else
4725 mesg=WM_COMMAND;
4729 if( mesg==WM_COMMAND )
4731 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
4732 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
4734 else if( mesg==WM_SYSCOMMAND )
4736 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
4737 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
4739 else
4741 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4742 * #0: unknown (please report!)
4743 * #1: for WM_KEYUP,WM_SYSKEYUP
4744 * #2: mouse is captured
4745 * #3: window is disabled
4746 * #4: it's a disabled system menu option
4747 * #5: it's a menu option, but window is iconic
4748 * #6: it's a menu option, but disabled
4750 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
4751 if(mesg==0)
4752 ERR_(accel)(" unknown reason - please report!\n");
4754 return TRUE;
4757 /**********************************************************************
4758 * TranslateAccelerator (USER32.@)
4759 * TranslateAcceleratorA (USER32.@)
4760 * TranslateAcceleratorW (USER32.@)
4762 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
4764 /* YES, Accel16! */
4765 LPACCEL16 lpAccelTbl;
4766 int i;
4768 if (msg == NULL)
4770 WARN_(accel)("msg null; should hang here to be win compatible\n");
4771 return 0;
4773 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
4775 WARN_(accel)("invalid accel handle=%p\n", hAccel);
4776 return 0;
4778 if ((msg->message != WM_KEYDOWN &&
4779 msg->message != WM_KEYUP &&
4780 msg->message != WM_SYSKEYDOWN &&
4781 msg->message != WM_SYSKEYUP &&
4782 msg->message != WM_CHAR)) return 0;
4784 TRACE_(accel)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4785 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4786 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
4788 i = 0;
4791 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
4792 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
4793 return 1;
4794 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
4795 WARN_(accel)("couldn't translate accelerator key\n");
4796 return 0;