user: Implement MNS_NOTIFYBYPOS.
[wine/gsoc_dplay.git] / dlls / user / menu.c
bloba28eff7e6576256180c21a9da4b1e1317c733f4d
1 /*
2 * Menu functions
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
42 #include "config.h"
43 #include "wine/port.h"
45 #include <stdarg.h>
46 #include <string.h>
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wingdi.h"
51 #include "winnls.h"
52 #include "wine/winbase16.h"
53 #include "wine/winuser16.h"
54 #include "wownt32.h"
55 #include "wine/server.h"
56 #include "wine/unicode.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
90 /* Popup menu structure */
91 typedef struct {
92 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
93 WORD wMagic; /* Magic number */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 SIZE maxBmpSize; /* Maximum size of the bitmap items */
113 } POPUPMENU, *LPPOPUPMENU;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x0001
118 #define TF_SUSPENDPOPUP 0x0002
119 #define TF_SKIPREMOVE 0x0004
121 typedef struct
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
130 #define MENU_MAGIC 0x554d /* 'MU' */
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
138 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
139 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
141 /* Space between 2 columns */
142 #define MENU_COL_SPACE 4
144 /* top and bottom margins for popup menus */
145 #define MENU_TOP_MARGIN 3
146 #define MENU_BOTTOM_MARGIN 2
148 /* (other menu->FocusedItem values give the position of the focused item) */
149 #define NO_SELECTED_ITEM 0xffff
151 #define MENU_ITEM_TYPE(flags) \
152 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
154 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
155 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
156 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
158 #define IS_SYSTEM_MENU(menu) \
159 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
161 #define MENUITEMINFO_TYPE_MASK \
162 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
165 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
166 #define STATE_MASK (~TYPE_MASK)
167 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
169 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
171 static SIZE menucharsize;
172 static UINT ODitemheight; /* default owner drawn item height */
174 /* Use global popup window because there's no way 2 menus can
175 * be tracked at the same time. */
176 static HWND top_popup;
178 /* Flag set by EndMenu() to force an exit from menu tracking */
179 static BOOL fEndMenu = FALSE;
181 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
183 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
185 /*********************************************************************
186 * menu class descriptor
188 const struct builtin_class_descr MENU_builtin_class =
190 POPUPMENU_CLASS_ATOMA, /* name */
191 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
192 NULL, /* procA (winproc is Unicode only) */
193 PopupMenuWndProc, /* procW */
194 sizeof(HMENU), /* extra */
195 IDC_ARROW, /* cursor */
196 (HBRUSH)(COLOR_MENU+1) /* brush */
200 /***********************************************************************
201 * debug_print_menuitem
203 * Print a menuitem in readable form.
206 #define debug_print_menuitem(pre, mp, post) \
207 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
209 #define MENUOUT(text) \
210 TRACE("%s%s", (count++ ? "," : ""), (text))
212 #define MENUFLAG(bit,text) \
213 do { \
214 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
215 } while (0)
217 static void do_debug_print_menuitem(const char *prefix, MENUITEM * mp,
218 const char *postfix)
220 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
221 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
222 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
223 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
224 TRACE("%s ", prefix);
225 if (mp) {
226 UINT flags = mp->fType;
227 TRACE( "{ ID=0x%x", mp->wID);
228 if ( mp->hSubMenu)
229 TRACE( ", Sub=%p", mp->hSubMenu);
230 if (flags) {
231 int count = 0;
232 TRACE( ", fType=");
233 MENUFLAG( MFT_SEPARATOR, "sep");
234 MENUFLAG( MFT_OWNERDRAW, "own");
235 MENUFLAG( MFT_BITMAP, "bit");
236 MENUFLAG(MF_POPUP, "pop");
237 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
238 MENUFLAG(MFT_MENUBREAK, "brk");
239 MENUFLAG(MFT_RADIOCHECK, "radio");
240 MENUFLAG(MFT_RIGHTORDER, "rorder");
241 MENUFLAG(MF_SYSMENU, "sys");
242 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
243 if (flags)
244 TRACE( "+0x%x", flags);
246 flags = mp->fState;
247 if (flags) {
248 int count = 0;
249 TRACE( ", State=");
250 MENUFLAG(MFS_GRAYED, "grey");
251 MENUFLAG(MFS_DEFAULT, "default");
252 MENUFLAG(MFS_DISABLED, "dis");
253 MENUFLAG(MFS_CHECKED, "check");
254 MENUFLAG(MFS_HILITE, "hi");
255 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
256 MENUFLAG(MF_MOUSESELECT, "mouse");
257 if (flags)
258 TRACE( "+0x%x", flags);
260 if (mp->hCheckBit)
261 TRACE( ", Chk=%p", mp->hCheckBit);
262 if (mp->hUnCheckBit)
263 TRACE( ", Unc=%p", mp->hUnCheckBit);
264 if (mp->text)
265 TRACE( ", Text=%s", debugstr_w(mp->text));
266 if (mp->dwItemData)
267 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
268 if (mp->hbmpItem)
270 if( IS_MAGIC_BITMAP(mp->hbmpItem))
271 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
272 else
273 TRACE( ", hbitmap=%p", mp->hbmpItem);
275 TRACE( " }");
276 } else
277 TRACE( "NULL");
278 TRACE(" %s\n", postfix);
281 #undef MENUOUT
282 #undef MENUFLAG
285 /***********************************************************************
286 * MENU_GetMenu
288 * Validate the given menu handle and returns the menu structure pointer.
290 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
292 POPUPMENU *menu = USER_HEAP_LIN_ADDR(hMenu);
293 if (!menu || menu->wMagic != MENU_MAGIC)
295 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu, menu, menu? menu->wMagic:0);
296 menu = NULL;
298 return menu;
301 /***********************************************************************
302 * get_win_sys_menu
304 * Get the system menu of a window
306 static HMENU get_win_sys_menu( HWND hwnd )
308 HMENU ret = 0;
309 WND *win = WIN_GetPtr( hwnd );
310 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 ret = win->hSysMenu;
313 WIN_ReleasePtr( win );
315 return ret;
318 /***********************************************************************
319 * get_menu_font
321 static HFONT get_menu_font( BOOL bold )
323 static HFONT hMenuFont, hMenuFontBold;
325 HFONT ret = bold ? hMenuFontBold : hMenuFont;
327 if (!ret)
329 NONCLIENTMETRICSW ncm;
330 HFONT prev;
332 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
333 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
335 if (bold)
337 ncm.lfMenuFont.lfWeight += 300;
338 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
340 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
341 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
342 ret, NULL );
343 if (prev)
345 /* another thread beat us to it */
346 DeleteObject( ret );
347 ret = prev;
350 return ret;
353 /***********************************************************************
354 * get_arrow_bitmap
356 static HBITMAP get_arrow_bitmap(void)
358 static HBITMAP arrow_bitmap;
360 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
361 return arrow_bitmap;
364 /***********************************************************************
365 * get_down_arrow_bitmap
367 static HBITMAP get_down_arrow_bitmap(void)
369 static HBITMAP arrow_bitmap;
371 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
372 return arrow_bitmap;
375 /***********************************************************************
376 * get_down_arrow_inactive_bitmap
378 static HBITMAP get_down_arrow_inactive_bitmap(void)
380 static HBITMAP arrow_bitmap;
382 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
383 return arrow_bitmap;
386 /***********************************************************************
387 * get_up_arrow_bitmap
389 static HBITMAP get_up_arrow_bitmap(void)
391 static HBITMAP arrow_bitmap;
393 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
394 return arrow_bitmap;
397 /***********************************************************************
398 * get_up_arrow_inactive_bitmap
400 static HBITMAP get_up_arrow_inactive_bitmap(void)
402 static HBITMAP arrow_bitmap;
404 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
405 return arrow_bitmap;
408 /***********************************************************************
409 * MENU_CopySysPopup
411 * Return the default system menu.
413 static HMENU MENU_CopySysPopup(void)
415 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
416 HMENU hMenu = LoadMenuW(user32_module, sysmenuW);
418 if( hMenu ) {
419 POPUPMENU* menu = MENU_GetMenu(hMenu);
420 menu->wFlags |= MF_SYSMENU | MF_POPUP;
421 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
423 else
424 ERR("Unable to load default system menu\n" );
426 TRACE("returning %p.\n", hMenu );
428 return hMenu;
432 /**********************************************************************
433 * MENU_GetSysMenu
435 * Create a copy of the system menu. System menu in Windows is
436 * a special menu bar with the single entry - system menu popup.
437 * This popup is presented to the outside world as a "system menu".
438 * However, the real system menu handle is sometimes seen in the
439 * WM_MENUSELECT parameters (and Word 6 likes it this way).
441 HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
443 HMENU hMenu;
445 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
446 if ((hMenu = CreateMenu()))
448 POPUPMENU *menu = MENU_GetMenu(hMenu);
449 menu->wFlags = MF_SYSMENU;
450 menu->hWnd = WIN_GetFullHandle( hWnd );
451 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
453 if (!hPopupMenu)
454 hPopupMenu = MENU_CopySysPopup();
456 if (hPopupMenu)
458 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
459 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
461 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
462 (UINT_PTR)hPopupMenu, NULL );
464 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
465 menu->items[0].fState = 0;
466 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
468 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
469 return hMenu;
471 DestroyMenu( hMenu );
473 ERR("failed to load system menu!\n");
474 return 0;
478 /***********************************************************************
479 * MENU_InitSysMenuPopup
481 * Grey the appropriate items in System menu.
483 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
485 BOOL gray;
487 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
488 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
489 gray = ((style & WS_MAXIMIZE) != 0);
490 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
491 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
492 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
493 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
494 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
495 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
496 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
497 gray = (clsStyle & CS_NOCLOSE) != 0;
499 /* The menu item must keep its state if it's disabled */
500 if(gray)
501 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
505 /******************************************************************************
507 * UINT MENU_GetStartOfNextColumn(
508 * HMENU hMenu )
510 *****************************************************************************/
512 static UINT MENU_GetStartOfNextColumn(
513 HMENU hMenu )
515 POPUPMENU *menu = MENU_GetMenu(hMenu);
516 UINT i;
518 if(!menu)
519 return NO_SELECTED_ITEM;
521 i = menu->FocusedItem + 1;
522 if( i == NO_SELECTED_ITEM )
523 return i;
525 for( ; i < menu->nItems; ++i ) {
526 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
527 return i;
530 return NO_SELECTED_ITEM;
534 /******************************************************************************
536 * UINT MENU_GetStartOfPrevColumn(
537 * HMENU hMenu )
539 *****************************************************************************/
541 static UINT MENU_GetStartOfPrevColumn(
542 HMENU hMenu )
544 POPUPMENU *menu = MENU_GetMenu(hMenu);
545 UINT i;
547 if( !menu )
548 return NO_SELECTED_ITEM;
550 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
551 return NO_SELECTED_ITEM;
553 /* Find the start of the column */
555 for(i = menu->FocusedItem; i != 0 &&
556 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
557 --i); /* empty */
559 if(i == 0)
560 return NO_SELECTED_ITEM;
562 for(--i; i != 0; --i) {
563 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
564 break;
567 TRACE("ret %d.\n", i );
569 return i;
574 /***********************************************************************
575 * MENU_FindItem
577 * Find a menu item. Return a pointer on the item, and modifies *hmenu
578 * in case the item was in a sub-menu.
580 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
582 POPUPMENU *menu;
583 MENUITEM *fallback = NULL;
584 UINT fallback_pos = 0;
585 UINT i;
587 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
588 if (wFlags & MF_BYPOSITION)
590 if (*nPos >= menu->nItems) return NULL;
591 return &menu->items[*nPos];
593 else
595 MENUITEM *item = menu->items;
596 for (i = 0; i < menu->nItems; i++, item++)
598 if (item->fType & MF_POPUP)
600 HMENU hsubmenu = item->hSubMenu;
601 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
602 if (subitem)
604 *hmenu = hsubmenu;
605 return subitem;
607 else if (item->wID == *nPos)
609 /* fallback to this item if nothing else found */
610 fallback_pos = i;
611 fallback = item;
614 else if (item->wID == *nPos)
616 *nPos = i;
617 return item;
622 if (fallback)
623 *nPos = fallback_pos;
625 return fallback;
628 /***********************************************************************
629 * MENU_FindSubMenu
631 * Find a Sub menu. Return the position of the submenu, and modifies
632 * *hmenu in case it is found in another sub-menu.
633 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
635 UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
637 POPUPMENU *menu;
638 UINT i;
639 MENUITEM *item;
640 if (((*hmenu)==(HMENU)0xffff) ||
641 (!(menu = MENU_GetMenu(*hmenu))))
642 return NO_SELECTED_ITEM;
643 item = menu->items;
644 for (i = 0; i < menu->nItems; i++, item++) {
645 if(!(item->fType & MF_POPUP)) continue;
646 if (item->hSubMenu == hSubTarget) {
647 return i;
649 else {
650 HMENU hsubmenu = item->hSubMenu;
651 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
652 if (pos != NO_SELECTED_ITEM) {
653 *hmenu = hsubmenu;
654 return pos;
658 return NO_SELECTED_ITEM;
661 /***********************************************************************
662 * MENU_FreeItemData
664 static void MENU_FreeItemData( MENUITEM* item )
666 /* delete text */
667 HeapFree( GetProcessHeap(), 0, item->text );
670 /***********************************************************************
671 * MENU_AdjustMenuItemRect
673 * Adjust menu item rectangle according to scrolling state.
675 static void
676 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
678 if (menu->bScrolling)
680 UINT arrow_bitmap_width, arrow_bitmap_height;
681 BITMAP bmp;
683 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
684 arrow_bitmap_width = bmp.bmWidth;
685 arrow_bitmap_height = bmp.bmHeight;
686 rect->top += arrow_bitmap_height - menu->nScrollPos;
687 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
692 /***********************************************************************
693 * MENU_FindItemByCoords
695 * Find the item at the specified coordinates (screen coords). Does
696 * not work for child windows and therefore should not be called for
697 * an arbitrary system menu.
699 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
700 POINT pt, UINT *pos )
702 MENUITEM *item;
703 UINT i;
704 RECT wrect;
705 RECT rect;
707 if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
708 pt.x -= wrect.left;pt.y -= wrect.top;
709 item = menu->items;
710 for (i = 0; i < menu->nItems; i++, item++)
712 rect = item->rect;
713 MENU_AdjustMenuItemRect(menu, &rect);
714 if ((pt.x >= rect.left) && (pt.x < rect.right) &&
715 (pt.y >= rect.top) && (pt.y < rect.bottom))
717 if (pos) *pos = i;
718 return item;
721 return NULL;
725 /***********************************************************************
726 * MENU_FindItemByKey
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
732 WCHAR key, BOOL forceMenuChar )
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
736 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
738 if (hmenu)
740 POPUPMENU *menu = MENU_GetMenu( hmenu );
741 MENUITEM *item = menu->items;
742 LRESULT menuchar;
744 if( !forceMenuChar )
746 UINT i;
748 for (i = 0; i < menu->nItems; i++, item++)
750 if( item->text)
752 WCHAR *p = item->text - 2;
755 p = strchrW (p + 2, '&');
757 while (p != NULL && p [1] == '&');
758 if (p && (toupperW(p[1]) == toupperW(key))) return i;
762 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
763 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
764 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
765 if (HIWORD(menuchar) == 1) return (UINT)(-2);
767 return (UINT)(-1);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
777 HWND hwndOwner)
779 BITMAP bm;
780 HBITMAP bmp = lpitem->hbmpItem;
782 size->cx = size->cy = 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR) bmp )
787 case (INT_PTR)HBMMENU_CALLBACK:
789 MEASUREITEMSTRUCT measItem;
790 measItem.CtlType = ODT_MENU;
791 measItem.CtlID = 0;
792 measItem.itemID = lpitem->wID;
793 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
794 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
795 measItem.itemData = lpitem->dwItemData;
796 SendMessageW( hwndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
797 size->cx = measItem.itemWidth;
798 size->cy = measItem.itemHeight;
799 return;
801 break;
802 case (INT_PTR)HBMMENU_SYSTEM:
803 if (lpitem->dwItemData)
805 bmp = (HBITMAP)lpitem->dwItemData;
806 break;
808 /* fall through */
809 case (INT_PTR)HBMMENU_MBAR_RESTORE:
810 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
811 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
812 case (INT_PTR)HBMMENU_MBAR_CLOSE:
813 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
814 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
815 size->cy = size->cx;
816 return;
817 case (INT_PTR)HBMMENU_POPUP_CLOSE:
818 case (INT_PTR)HBMMENU_POPUP_RESTORE:
819 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
820 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
821 FIXME("Magic %p not implemented\n", bmp );
822 return;
824 if (GetObjectW(bmp, sizeof(bm), &bm ))
826 size->cx = bm.bmWidth;
827 size->cy = bm.bmHeight;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
837 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
839 BITMAP bm;
840 DWORD rop;
841 HDC hdcMem;
842 HBITMAP bmp;
843 int w = rect->right - rect->left;
844 int h = rect->bottom - rect->top;
845 int bmp_xoffset = 0;
846 int left, top;
847 HBITMAP hbmToDraw = lpitem->hbmpItem;
848 bmp = hbmToDraw;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw))
853 UINT flags = 0;
854 RECT r;
856 switch((INT_PTR)hbmToDraw)
858 case (INT_PTR)HBMMENU_SYSTEM:
859 if (lpitem->dwItemData)
861 bmp = (HBITMAP)lpitem->dwItemData;
862 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
864 else
866 static HBITMAP hBmpSysMenu;
868 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
869 bmp = hBmpSysMenu;
870 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset = bm.bmWidth / 2;
873 bm.bmWidth -= bmp_xoffset;
875 goto got_bitmap;
876 case (INT_PTR)HBMMENU_MBAR_RESTORE:
877 flags = DFCS_CAPTIONRESTORE;
878 break;
879 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
880 flags = DFCS_CAPTIONMIN;
881 break;
882 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
883 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
884 break;
885 case (INT_PTR)HBMMENU_MBAR_CLOSE:
886 flags = DFCS_CAPTIONCLOSE;
887 break;
888 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
889 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
890 break;
891 case (INT_PTR)HBMMENU_CALLBACK:
893 DRAWITEMSTRUCT drawItem;
894 drawItem.CtlType = ODT_MENU;
895 drawItem.CtlID = 0;
896 drawItem.itemID = lpitem->wID;
897 drawItem.itemAction = odaction;
898 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
899 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
900 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
901 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
902 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
903 drawItem.hwndItem = (HWND)hmenu;
904 drawItem.hDC = hdc;
905 drawItem.itemData = lpitem->dwItemData;
906 drawItem.rcItem = *rect;
907 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
908 return;
910 break;
911 case (INT_PTR)HBMMENU_POPUP_CLOSE:
912 case (INT_PTR)HBMMENU_POPUP_RESTORE:
913 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
914 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
915 default:
916 FIXME("Magic %p not implemented\n", hbmToDraw);
917 return;
919 r = *rect;
920 InflateRect( &r, -1, -1 );
921 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
922 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
923 return;
926 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
928 got_bitmap:
929 hdcMem = CreateCompatibleDC( hdc );
930 SelectObject( hdcMem, bmp );
932 /* handle fontsize > bitmap_height */
933 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
934 left=rect->left;
935 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
936 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
937 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
938 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
939 DeleteDC( hdcMem );
943 /***********************************************************************
944 * MENU_CalcItemSize
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
949 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
951 WCHAR *p;
952 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
953 UINT arrow_bitmap_width;
954 BITMAP bm;
955 INT itemheight;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
959 (menuBar ? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
962 arrow_bitmap_width = bm.bmWidth;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize.cx ) {
966 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight = HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
974 if (lpitem->fType & MF_OWNERDRAW)
976 MEASUREITEMSTRUCT mis;
977 mis.CtlType = ODT_MENU;
978 mis.CtlID = 0;
979 mis.itemID = lpitem->wID;
980 mis.itemData = lpitem->dwItemData;
981 mis.itemHeight = ODitemheight;
982 mis.itemWidth = 0;
983 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
988 if (menuBar) {
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
992 } else
993 lpitem->rect.bottom += mis.itemHeight;
995 TRACE("id=%04x size=%ldx%ld\n",
996 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
997 lpitem->rect.bottom-lpitem->rect.top);
998 return;
1001 if (lpitem->fType & MF_SEPARATOR)
1003 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1004 if( !menuBar)
1005 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1006 return;
1009 itemheight = 0;
1010 lpitem->xTab = 0;
1012 if (!menuBar) {
1013 if (lpitem->hbmpItem) {
1014 SIZE size;
1016 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem->bmpsize = size;
1020 lppop->maxBmpSize.cx = max( lppop->maxBmpSize.cx, size.cx);
1021 lppop->maxBmpSize.cy = max( lppop->maxBmpSize.cy, size.cy);
1022 lpitem->rect.right += size.cx + 2;
1023 itemheight = size.cy + 2;
1025 if( !(lppop->dwStyle & MNS_NOCHECK))
1026 lpitem->rect.right += check_bitmap_width;
1027 lpitem->rect.right += 4 + menucharsize.cx;
1028 lpitem->xTab = lpitem->rect.right;
1029 lpitem->rect.right += arrow_bitmap_width;
1030 } else if (lpitem->hbmpItem) { /* menuBar */
1031 SIZE size;
1033 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1034 lpitem->bmpsize = size;
1035 lpitem->rect.right += size.cx;
1036 if( lpitem->text) lpitem->rect.right += 2;
1037 itemheight = size.cy;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1042 HFONT hfontOld = NULL;
1043 RECT rc = lpitem->rect;
1044 LONG txtheight, txtwidth;
1046 if ( lpitem->fState & MFS_DEFAULT ) {
1047 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1049 if (menuBar) {
1050 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1051 DT_SINGLELINE|DT_CALCRECT);
1052 lpitem->rect.right += rc.right - rc.left;
1053 itemheight = max( max( itemheight, txtheight),
1054 GetSystemMetrics( SM_CYMENU) - 1);
1055 lpitem->rect.right += 2 * menucharsize.cx;
1056 } else {
1057 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1058 RECT tmprc = rc;
1059 LONG tmpheight;
1060 int n = (int)( p - lpitem->text);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1064 DT_SINGLELINE|DT_CALCRECT);
1065 txtwidth = rc.right - rc.left;
1066 p += 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1069 DT_SINGLELINE|DT_CALCRECT);
1070 lpitem->xTab += txtwidth;
1071 txtheight = max( txtheight, tmpheight);
1072 txtwidth += menucharsize.cx + /* space for the tab */
1073 tmprc.right - tmprc.left; /* space for the short cut */
1074 } else {
1075 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1076 DT_SINGLELINE|DT_CALCRECT);
1077 txtwidth = rc.right - rc.left;
1078 lpitem->xTab += txtwidth;
1080 lpitem->rect.right += 2 + txtwidth;
1081 itemheight = max( itemheight,
1082 max( txtheight + 2, menucharsize.cy + 4));
1084 if (hfontOld) SelectObject (hdc, hfontOld);
1085 } else if( menuBar) {
1086 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1088 lpitem->rect.bottom += itemheight;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1096 static UINT
1097 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
1099 if (lppop->cyMax)
1100 return lppop->cyMax;
1101 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
1112 MENUITEM *lpitem;
1113 HDC hdc;
1114 int start, i;
1115 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1117 lppop->Width = lppop->Height = 0;
1118 if (lppop->nItems == 0) return;
1119 hdc = GetDC( 0 );
1121 SelectObject( hdc, get_menu_font(FALSE));
1123 start = 0;
1124 maxX = 2 + 1;
1126 lppop->maxBmpSize.cx = 0;
1127 lppop->maxBmpSize.cy = 0;
1129 while (start < lppop->nItems)
1131 lpitem = &lppop->items[start];
1132 orgX = maxX;
1133 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1134 orgX += MENU_COL_SPACE;
1135 orgY = MENU_TOP_MARGIN;
1137 maxTab = maxTabWidth = 0;
1138 /* Parse items until column break or end of menu */
1139 for (i = start; i < lppop->nItems; i++, lpitem++)
1141 if ((i != start) &&
1142 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1144 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE, lppop );
1145 maxX = max( maxX, lpitem->rect.right );
1146 orgY = lpitem->rect.bottom;
1147 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1149 maxTab = max( maxTab, lpitem->xTab );
1150 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX = max( maxX, maxTab + maxTabWidth );
1156 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1158 lpitem->rect.right = maxX;
1159 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1160 lpitem->xTab = maxTab;
1163 lppop->Height = max( lppop->Height, orgY );
1166 lppop->Width = maxX;
1168 /* space for 3d border */
1169 lppop->Height += MENU_BOTTOM_MARGIN;
1170 lppop->Width += 2;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight = MENU_GetMaxPopupHeight(lppop);
1174 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1175 if (lppop->Height >= maxHeight)
1177 lppop->Height = maxHeight;
1178 lppop->bScrolling = TRUE;
1180 else
1182 lppop->bScrolling = FALSE;
1185 ReleaseDC( 0, hdc );
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1199 LPPOPUPMENU lppop, HWND hwndOwner )
1201 MENUITEM *lpitem;
1202 int start, i, orgX, orgY, maxY, helpPos;
1204 if ((lprect == NULL) || (lppop == NULL)) return;
1205 if (lppop->nItems == 0) return;
1206 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1207 lppop->Width = lprect->right - lprect->left;
1208 lppop->Height = 0;
1209 maxY = lprect->top+1;
1210 start = 0;
1211 helpPos = -1;
1212 lppop->maxBmpSize.cx = 0;
1213 lppop->maxBmpSize.cy = 0;
1214 while (start < lppop->nItems)
1216 lpitem = &lppop->items[start];
1217 orgX = lprect->left;
1218 orgY = maxY;
1220 /* Parse items until line break or end of menu */
1221 for (i = start; i < lppop->nItems; i++, lpitem++)
1223 if ((helpPos == -1) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1224 if ((i != start) &&
1225 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1228 debug_print_menuitem (" item: ", lpitem, "");
1229 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1231 if (lpitem->rect.right > lprect->right)
1233 if (i != start) break;
1234 else lpitem->rect.right = lprect->right;
1236 maxY = max( maxY, lpitem->rect.bottom );
1237 orgX = lpitem->rect.right;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start < i) lppop->items[start++].rect.bottom = maxY;
1244 lprect->bottom = maxY;
1245 lppop->Height = lprect->bottom - lprect->top;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem = &lppop->items[lppop->nItems-1];
1250 orgY = lpitem->rect.top;
1251 orgX = lprect->right;
1252 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1253 if ( (helpPos==-1) || (helpPos>i) )
1254 break; /* done */
1255 if (lpitem->rect.top != orgY) break; /* Other line */
1256 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1257 lpitem->rect.left += orgX - lpitem->rect.right;
1258 lpitem->rect.right = orgX;
1259 orgX = lpitem->rect.left;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1269 static void
1270 MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
1272 HDC hdcMem = CreateCompatibleDC(hdc);
1273 HBITMAP hOrigBitmap;
1274 UINT arrow_bitmap_width, arrow_bitmap_height;
1275 BITMAP bmp;
1276 RECT rect;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1279 arrow_bitmap_width = bmp.bmWidth;
1280 arrow_bitmap_height = bmp.bmHeight;
1283 if (lppop->nScrollPos)
1284 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1285 else
1286 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1287 rect.left = 0;
1288 rect.top = 0;
1289 rect.right = lppop->Width;
1290 rect.bottom = arrow_bitmap_height;
1291 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1292 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1293 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1294 rect.top = lppop->Height - arrow_bitmap_height;
1295 rect.bottom = lppop->Height;
1296 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1297 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1298 SelectObject(hdcMem, get_down_arrow_bitmap());
1299 else
1300 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1302 lppop->Height - arrow_bitmap_height,
1303 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1304 SelectObject(hdcMem, hOrigBitmap);
1305 DeleteDC(hdcMem);
1309 /***********************************************************************
1310 * draw_popup_arrow
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1315 UINT arrow_bitmap_height)
1317 HDC hdcMem = CreateCompatibleDC( hdc );
1318 HBITMAP hOrigBitmap;
1320 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1321 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1322 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1323 arrow_bitmap_width, arrow_bitmap_height,
1324 hdcMem, 0, 0, SRCCOPY );
1325 SelectObject( hdcMem, hOrigBitmap );
1326 DeleteDC( hdcMem );
1328 /***********************************************************************
1329 * MENU_DrawMenuItem
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1334 UINT height, BOOL menuBar, UINT odaction )
1336 RECT rect;
1337 BOOL flat_menu = FALSE;
1338 int bkgnd;
1339 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1340 POPUPMENU *menu = MENU_GetMenu(hmenu);
1341 RECT bmprc;
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1345 if (!menuBar) {
1346 BITMAP bmp;
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1348 arrow_bitmap_width = bmp.bmWidth;
1349 arrow_bitmap_height = bmp.bmHeight;
1352 if (lpitem->fType & MF_SYSMENU)
1354 if( !IsIconic(hwnd) )
1355 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1356 return;
1359 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1360 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1362 /* Setup colors */
1364 if (lpitem->fState & MF_HILITE)
1366 if(menuBar && !flat_menu) {
1367 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1368 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1369 } else {
1370 if(lpitem->fState & MF_GRAYED)
1371 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1372 else
1373 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1374 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1377 else
1379 if (lpitem->fState & MF_GRAYED)
1380 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1381 else
1382 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1383 SetBkColor( hdc, GetSysColor( bkgnd ) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1387 rect = lpitem->rect;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1390 if (lpitem->fType & MF_OWNERDRAW)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1401 DRAWITEMSTRUCT dis;
1403 dis.CtlType = ODT_MENU;
1404 dis.CtlID = 0;
1405 dis.itemID = lpitem->wID;
1406 dis.itemData = lpitem->dwItemData;
1407 dis.itemState = 0;
1408 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1409 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1410 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1411 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis.hwndItem = (HWND)hmenu;
1413 dis.hDC = hdc;
1414 dis.rcItem = rect;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1417 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1418 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1419 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1420 /* Draw the popup-menu arrow */
1421 if (lpitem->fType & MF_POPUP)
1422 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1423 arrow_bitmap_height);
1424 return;
1427 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1429 if (lpitem->fState & MF_HILITE)
1431 if (flat_menu)
1433 InflateRect (&rect, -1, -1);
1434 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1435 InflateRect (&rect, 1, 1);
1436 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1438 else
1440 if(menuBar)
1441 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1442 else
1443 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1446 else
1447 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1449 SetBkMode( hdc, TRANSPARENT );
1451 /* vertical separator */
1452 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1454 HPEN oldPen;
1455 RECT rc = rect;
1457 rc.left -= MENU_COL_SPACE / 2 + 1;
1458 rc.top = 3;
1459 rc.bottom = height - 3;
1460 if (flat_menu)
1462 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1463 MoveToEx( hdc, rc.left, rc.top, NULL );
1464 LineTo( hdc, rc.left, rc.bottom );
1465 SelectObject( hdc, oldPen );
1467 else
1468 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1471 /* horizontal separator */
1472 if (lpitem->fType & MF_SEPARATOR)
1474 HPEN oldPen;
1475 RECT rc = rect;
1477 rc.left++;
1478 rc.right--;
1479 rc.top = ( rc.top + rc.bottom) / 2;
1480 if (flat_menu)
1482 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1483 MoveToEx( hdc, rc.left, rc.top, NULL );
1484 LineTo( hdc, rc.right, rc.top );
1485 SelectObject( hdc, oldPen );
1487 else
1488 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1489 return;
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem->hbmpItem) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1502 if( menuBar) {
1503 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1504 bmprc.left = 3;
1505 else
1506 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1507 } else {
1508 bmprc.left = 4;
1509 if( !(menu->dwStyle & ( MNS_CHECKORBMP | MNS_NOCHECK)))
1510 bmprc.left += GetSystemMetrics( SM_CXMENUCHECK);
1512 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1513 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1514 bmprc.top = 0;
1515 else
1516 bmprc.top = (lpitem->rect.bottom - lpitem->rect.top -
1517 lpitem->bmpsize.cy) / 2;
1518 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1521 if (!menuBar)
1523 HBITMAP bm;
1524 INT y = rect.top + rect.bottom;
1525 RECT rc = rect;
1526 int checked = FALSE;
1527 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1528 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1529 /* Draw the check mark
1531 * FIXME:
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu->dwStyle & MNS_NOCHECK)) {
1535 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1536 lpitem->hUnCheckBit;
1537 if (bm) /* we have a custom bitmap */
1539 HDC hdcMem = CreateCompatibleDC( hdc );
1541 SelectObject( hdcMem, bm );
1542 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1543 check_bitmap_width, check_bitmap_height,
1544 hdcMem, 0, 0, SRCCOPY );
1545 DeleteDC( hdcMem );
1546 checked = TRUE;
1548 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1550 RECT r;
1551 HBITMAP bm = CreateBitmap( check_bitmap_width,
1552 check_bitmap_height, 1, 1, NULL );
1553 HDC hdcMem = CreateCompatibleDC( hdc );
1555 SelectObject( hdcMem, bm );
1556 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1557 DrawFrameControl( hdcMem, &r, DFC_MENU,
1558 (lpitem->fType & MFT_RADIOCHECK) ?
1559 DFCS_MENUBULLET : DFCS_MENUCHECK );
1560 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1561 hdcMem, 0, 0, SRCCOPY );
1562 DeleteDC( hdcMem );
1563 DeleteObject( bm );
1564 checked = TRUE;
1567 if( lpitem->hbmpItem &&
1568 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1569 POINT origorg;
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1572 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1573 odaction, FALSE);
1574 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem->fType & MF_POPUP)
1578 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1579 arrow_bitmap_height);
1580 rect.left += 4;
1581 if( !(menu->dwStyle & MNS_NOCHECK))
1582 rect.left += check_bitmap_width;
1583 rect.right -= arrow_bitmap_width;
1585 else if( lpitem->hbmpItem)
1586 { /* Draw the bitmap */
1587 POINT origorg;
1589 SetViewportOrgEx( hdc, lpitem->rect.left, lpitem->rect.top, &origorg);
1590 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1591 odaction, menuBar);
1592 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1594 /* process text if present */
1595 if (lpitem->text)
1597 register int i;
1598 HFONT hfontOld = 0;
1600 UINT uFormat = (menuBar) ?
1601 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1602 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1604 if( !(menu->dwStyle & MNS_CHECKORBMP))
1605 rect.left += menu->maxBmpSize.cx;
1607 if ( lpitem->fState & MFS_DEFAULT )
1609 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1612 if (menuBar) {
1613 if( lpitem->hbmpItem)
1614 rect.left += lpitem->bmpsize.cx;
1615 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1616 rect.left += menucharsize.cx;
1617 rect.right -= menucharsize.cx;
1620 for (i = 0; lpitem->text[i]; i++)
1621 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1622 break;
1624 if(lpitem->fState & MF_GRAYED)
1626 if (!(lpitem->fState & MF_HILITE) )
1628 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1629 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1631 --rect.left; --rect.top; --rect.right; --rect.bottom;
1633 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1638 /* paint the shortcut text */
1639 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1641 if (lpitem->text[i] == '\t')
1643 rect.left = lpitem->xTab;
1644 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1646 else
1648 rect.right = lpitem->xTab;
1649 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1652 if(lpitem->fState & MF_GRAYED)
1654 if (!(lpitem->fState & MF_HILITE) )
1656 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1657 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1659 --rect.left; --rect.top; --rect.right; --rect.bottom;
1661 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1666 if (hfontOld)
1667 SelectObject (hdc, hfontOld);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1679 HBRUSH hPrevBrush = 0;
1680 RECT rect;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1684 GetClientRect( hwnd, &rect );
1686 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1687 && (SelectObject( hdc, get_menu_font(FALSE))))
1689 HPEN hPrevPen;
1691 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1693 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1694 if( hPrevPen )
1696 POPUPMENU *menu;
1697 BOOL flat_menu = FALSE;
1699 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1700 if (flat_menu)
1701 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1702 else
1703 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1705 if( (menu = MENU_GetMenu( hmenu )))
1707 /* draw menu items */
1708 if( menu->nItems)
1710 MENUITEM *item;
1711 UINT u;
1713 item = menu->items;
1714 for( u = menu->nItems; u > 0; u--, item++)
1715 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1716 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1718 /* draw scroll arrows */
1719 if (menu->bScrolling)
1720 MENU_DrawScrollArrows(menu, hdc);
1722 } else
1724 SelectObject( hdc, hPrevBrush );
1729 /***********************************************************************
1730 * MENU_DrawMenuBar
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1736 BOOL suppress_draw)
1738 LPPOPUPMENU lppop;
1739 HFONT hfontOld = 0;
1740 HMENU hMenu = GetMenu(hwnd);
1742 lppop = MENU_GetMenu( hMenu );
1743 if (lppop == NULL || lprect == NULL)
1745 return GetSystemMetrics(SM_CYMENU);
1748 if (suppress_draw)
1750 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1752 if (lppop->Height == 0)
1753 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1755 lprect->bottom = lprect->top + lppop->Height;
1757 if (hfontOld) SelectObject( hDC, hfontOld);
1758 return lppop->Height;
1760 else
1761 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1765 /***********************************************************************
1766 * MENU_ShowPopup
1768 * Display a popup menu.
1770 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id,
1771 INT x, INT y, INT xanchor, INT yanchor )
1773 POPUPMENU *menu;
1774 UINT width, height;
1776 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1777 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1779 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1780 if (menu->FocusedItem != NO_SELECTED_ITEM)
1782 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1783 menu->FocusedItem = NO_SELECTED_ITEM;
1786 /* store the owner for DrawItem */
1787 menu->hwndOwner = hwndOwner;
1789 menu->nScrollPos = 0;
1790 MENU_PopupMenuCalcSize( menu, hwndOwner );
1792 /* adjust popup menu pos so that it fits within the desktop */
1794 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1795 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1797 if( x + width > GetSystemMetrics(SM_CXSCREEN ))
1799 if( xanchor && x >= width - xanchor )
1800 x -= width - xanchor;
1802 if( x + width > GetSystemMetrics(SM_CXSCREEN))
1803 x = GetSystemMetrics(SM_CXSCREEN) - width;
1805 if( x < 0 ) x = 0;
1807 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1809 if( yanchor && y >= height + yanchor )
1810 y -= height + yanchor;
1812 if( y + height > GetSystemMetrics(SM_CYSCREEN ))
1813 y = GetSystemMetrics(SM_CYSCREEN) - height;
1815 if( y < 0 ) y = 0;
1817 /* NOTE: In Windows, top menu popup is not owned. */
1818 menu->hWnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1819 WS_POPUP, x, y, width, height,
1820 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1821 (LPVOID)hmenu );
1822 if( !menu->hWnd ) return FALSE;
1823 if (!top_popup) top_popup = menu->hWnd;
1825 /* Display the window */
1827 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
1828 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1829 UpdateWindow( menu->hWnd );
1830 return TRUE;
1834 /***********************************************************************
1835 * MENU_EnsureMenuItemVisible
1837 static void
1838 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1840 if (lppop->bScrolling)
1842 MENUITEM *item = &lppop->items[wIndex];
1843 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1844 UINT nOldPos = lppop->nScrollPos;
1845 RECT rc;
1846 UINT arrow_bitmap_height;
1847 BITMAP bmp;
1849 GetClientRect(lppop->hWnd, &rc);
1851 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1852 arrow_bitmap_height = bmp.bmHeight;
1854 rc.top += arrow_bitmap_height;
1855 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1857 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1858 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1861 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1862 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1863 MENU_DrawScrollArrows(lppop, hdc);
1865 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1867 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1868 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1869 MENU_DrawScrollArrows(lppop, hdc);
1875 /***********************************************************************
1876 * MENU_SelectItem
1878 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1879 BOOL sendMenuSelect, HMENU topmenu )
1881 LPPOPUPMENU lppop;
1882 HDC hdc;
1884 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1886 lppop = MENU_GetMenu( hmenu );
1887 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1889 if (lppop->FocusedItem == wIndex) return;
1890 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1891 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1892 if (!top_popup) top_popup = lppop->hWnd;
1894 SelectObject( hdc, get_menu_font(FALSE));
1896 /* Clear previous highlighted item */
1897 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1899 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1900 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
1901 lppop->Height, !(lppop->wFlags & MF_POPUP),
1902 ODA_SELECT );
1905 /* Highlight new item (if any) */
1906 lppop->FocusedItem = wIndex;
1907 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1909 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
1910 lppop->items[wIndex].fState |= MF_HILITE;
1911 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
1912 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
1913 &lppop->items[wIndex], lppop->Height,
1914 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
1916 if (sendMenuSelect)
1918 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
1919 SendMessageW( hwndOwner, WM_MENUSELECT,
1920 MAKELONG(ip->fType & MF_POPUP ? wIndex: ip->wID,
1921 ip->fType | ip->fState |
1922 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
1925 else if (sendMenuSelect) {
1926 if(topmenu){
1927 int pos;
1928 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
1929 POPUPMENU *ptm = MENU_GetMenu( topmenu );
1930 MENUITEM *ip = &ptm->items[pos];
1931 SendMessageW( hwndOwner, WM_MENUSELECT, MAKELONG(pos,
1932 ip->fType | ip->fState |
1933 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
1937 ReleaseDC( lppop->hWnd, hdc );
1941 /***********************************************************************
1942 * MENU_MoveSelection
1944 * Moves currently selected item according to the offset parameter.
1945 * If there is no selection then it should select the last item if
1946 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1948 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
1950 INT i;
1951 POPUPMENU *menu;
1953 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
1955 menu = MENU_GetMenu( hmenu );
1956 if ((!menu) || (!menu->items)) return;
1958 if ( menu->FocusedItem != NO_SELECTED_ITEM )
1960 if( menu->nItems == 1 ) return; else
1961 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
1962 ; i += offset)
1963 if (!(menu->items[i].fType & MF_SEPARATOR))
1965 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1966 return;
1970 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
1971 i >= 0 && i < menu->nItems ; i += offset)
1972 if (!(menu->items[i].fType & MF_SEPARATOR))
1974 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
1975 return;
1980 /**********************************************************************
1981 * MENU_SetItemData
1983 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1984 * ModifyMenu().
1986 static BOOL MENU_SetItemData( MENUITEM *item, UINT flags, UINT_PTR id,
1987 LPCWSTR str )
1989 debug_print_menuitem("MENU_SetItemData from: ", item, "");
1990 TRACE("flags=%x str=%p\n", flags, str);
1992 if (IS_STRING_ITEM(flags))
1994 LPWSTR prevText = item->text;
1995 if (!str)
1997 flags |= MF_SEPARATOR;
1998 item->text = NULL;
2000 else
2002 LPWSTR text;
2003 /* Item beginning with a backspace is a help item */
2004 if (*str == '\b')
2006 flags |= MF_HELP;
2007 str++;
2009 if (!(text = HeapAlloc( GetProcessHeap(), 0, (strlenW(str)+1) * sizeof(WCHAR) )))
2010 return FALSE;
2011 strcpyW( text, str );
2012 item->text = text;
2014 item->hbmpItem = NULL;
2015 HeapFree( GetProcessHeap(), 0, prevText );
2017 else if(( flags & MFT_BITMAP)) {
2018 item->hbmpItem = HBITMAP_32(LOWORD(str));
2019 /* setting bitmap clears text */
2020 HeapFree( GetProcessHeap(), 0, item->text );
2021 item->text = NULL;
2024 if (flags & MF_OWNERDRAW)
2025 item->dwItemData = (DWORD_PTR)str;
2026 else
2027 item->dwItemData = 0;
2029 if ((item->fType & MF_POPUP) && (flags & MF_POPUP) && (item->hSubMenu != (HMENU)id) )
2030 DestroyMenu( item->hSubMenu ); /* ModifyMenu() spec */
2032 if (flags & MF_POPUP)
2034 POPUPMENU *menu = MENU_GetMenu((HMENU)id);
2035 if (menu) menu->wFlags |= MF_POPUP;
2036 else
2038 item->wID = 0;
2039 item->hSubMenu = 0;
2040 item->fType = 0;
2041 item->fState = 0;
2042 return FALSE;
2046 item->wID = id;
2047 if (flags & MF_POPUP) item->hSubMenu = (HMENU)id;
2049 if ((item->fType & MF_POPUP) && !(flags & MF_POPUP) )
2050 flags |= MF_POPUP; /* keep popup */
2052 item->fType = flags & TYPE_MASK;
2053 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2054 for ModifyMenu, but Windows accepts it */
2055 item->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
2057 /* Don't call SetRectEmpty here! */
2059 debug_print_menuitem("MENU_SetItemData to : ", item, "");
2060 return TRUE;
2064 /**********************************************************************
2065 * MENU_InsertItem
2067 * Insert (allocate) a new item into a menu.
2069 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2071 MENUITEM *newItems;
2072 POPUPMENU *menu;
2074 if (!(menu = MENU_GetMenu(hMenu)))
2075 return NULL;
2077 /* Find where to insert new item */
2079 if (flags & MF_BYPOSITION) {
2080 if (pos > menu->nItems)
2081 pos = menu->nItems;
2082 } else {
2083 if (!MENU_FindItem( &hMenu, &pos, flags ))
2084 pos = menu->nItems;
2085 else {
2086 if (!(menu = MENU_GetMenu( hMenu )))
2087 return NULL;
2091 /* Create new items array */
2093 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2094 if (!newItems)
2096 WARN("allocation failed\n" );
2097 return NULL;
2099 if (menu->nItems > 0)
2101 /* Copy the old array into the new one */
2102 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2103 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2104 (menu->nItems-pos)*sizeof(MENUITEM) );
2105 HeapFree( GetProcessHeap(), 0, menu->items );
2107 menu->items = newItems;
2108 menu->nItems++;
2109 memset( &newItems[pos], 0, sizeof(*newItems) );
2110 menu->Height = 0; /* force size recalculate */
2111 return &newItems[pos];
2115 /**********************************************************************
2116 * MENU_ParseResource
2118 * Parse a standard menu resource and add items to the menu.
2119 * Return a pointer to the end of the resource.
2121 * NOTE: flags is equivalent to the mtOption field
2123 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
2125 WORD flags, id = 0;
2126 LPCSTR str;
2127 BOOL end_flag;
2131 flags = GET_WORD(res);
2132 end_flag = flags & MF_END;
2133 /* Remove MF_END because it has the same value as MF_HILITE */
2134 flags &= ~MF_END;
2135 res += sizeof(WORD);
2136 if (!(flags & MF_POPUP))
2138 id = GET_WORD(res);
2139 res += sizeof(WORD);
2141 str = res;
2142 if (!unicode) res += strlen(str) + 1;
2143 else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2144 if (flags & MF_POPUP)
2146 HMENU hSubMenu = CreatePopupMenu();
2147 if (!hSubMenu) return NULL;
2148 if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
2149 return NULL;
2150 if (!unicode) AppendMenuA( hMenu, flags, (UINT_PTR)hSubMenu, str );
2151 else AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str );
2153 else /* Not a popup */
2155 if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
2156 else AppendMenuW( hMenu, flags, id,
2157 *(LPCWSTR)str ? (LPCWSTR)str : NULL );
2159 } while (!end_flag);
2160 return res;
2164 /**********************************************************************
2165 * MENUEX_ParseResource
2167 * Parse an extended menu resource and add items to the menu.
2168 * Return a pointer to the end of the resource.
2170 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2172 WORD resinfo;
2173 do {
2174 MENUITEMINFOW mii;
2176 mii.cbSize = sizeof(mii);
2177 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2178 mii.fType = GET_DWORD(res);
2179 res += sizeof(DWORD);
2180 mii.fState = GET_DWORD(res);
2181 res += sizeof(DWORD);
2182 mii.wID = GET_DWORD(res);
2183 res += sizeof(DWORD);
2184 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2185 res += sizeof(WORD);
2186 /* Align the text on a word boundary. */
2187 res += (~((UINT_PTR)res - 1)) & 1;
2188 mii.dwTypeData = (LPWSTR) res;
2189 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2190 /* Align the following fields on a dword boundary. */
2191 res += (~((UINT_PTR)res - 1)) & 3;
2193 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2194 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2196 if (resinfo & 1) { /* Pop-up? */
2197 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2198 res += sizeof(DWORD);
2199 mii.hSubMenu = CreatePopupMenu();
2200 if (!mii.hSubMenu)
2201 return NULL;
2202 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2203 DestroyMenu(mii.hSubMenu);
2204 return NULL;
2206 mii.fMask |= MIIM_SUBMENU;
2207 mii.fType |= MF_POPUP;
2209 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2211 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2212 mii.wID, mii.fType);
2213 mii.fType |= MF_SEPARATOR;
2215 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2216 } while (!(resinfo & MF_END));
2217 return res;
2221 /***********************************************************************
2222 * MENU_GetSubPopup
2224 * Return the handle of the selected sub-popup menu (if any).
2226 static HMENU MENU_GetSubPopup( HMENU hmenu )
2228 POPUPMENU *menu;
2229 MENUITEM *item;
2231 menu = MENU_GetMenu( hmenu );
2233 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2235 item = &menu->items[menu->FocusedItem];
2236 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2237 return item->hSubMenu;
2238 return 0;
2242 /***********************************************************************
2243 * MENU_HideSubPopups
2245 * Hide the sub-popup menus of this menu.
2247 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2248 BOOL sendMenuSelect )
2250 POPUPMENU *menu = MENU_GetMenu( hmenu );
2252 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2254 if (menu && top_popup)
2256 HMENU hsubmenu;
2257 POPUPMENU *submenu;
2258 MENUITEM *item;
2260 if (menu->FocusedItem != NO_SELECTED_ITEM)
2262 item = &menu->items[menu->FocusedItem];
2263 if (!(item->fType & MF_POPUP) ||
2264 !(item->fState & MF_MOUSESELECT)) return;
2265 item->fState &= ~MF_MOUSESELECT;
2266 hsubmenu = item->hSubMenu;
2267 } else return;
2269 submenu = MENU_GetMenu( hsubmenu );
2270 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE );
2271 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2272 DestroyWindow( submenu->hWnd );
2273 submenu->hWnd = 0;
2278 /***********************************************************************
2279 * MENU_ShowSubPopup
2281 * Display the sub-menu of the selected item of this menu.
2282 * Return the handle of the submenu, or hmenu if no submenu to display.
2284 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2285 BOOL selectFirst, UINT wFlags )
2287 RECT rect;
2288 POPUPMENU *menu;
2289 MENUITEM *item;
2290 HDC hdc;
2292 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2294 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2296 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2298 item = &menu->items[menu->FocusedItem];
2299 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2300 return hmenu;
2302 /* message must be sent before using item,
2303 because nearly everything may be changed by the application ! */
2305 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2306 if (!(wFlags & TPM_NONOTIFY))
2307 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2308 MAKELONG( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2310 item = &menu->items[menu->FocusedItem];
2311 rect = item->rect;
2313 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2314 if (!(item->fState & MF_HILITE))
2316 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2317 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2319 SelectObject( hdc, get_menu_font(FALSE));
2321 item->fState |= MF_HILITE;
2322 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2323 ReleaseDC( menu->hWnd, hdc );
2325 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2326 item->rect = rect;
2328 item->fState |= MF_MOUSESELECT;
2330 if (IS_SYSTEM_MENU(menu))
2332 MENU_InitSysMenuPopup(item->hSubMenu,
2333 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2334 GetClassLongW( menu->hWnd, GCL_STYLE));
2336 NC_GetSysPopupPos( menu->hWnd, &rect );
2337 rect.top = rect.bottom;
2338 rect.right = GetSystemMetrics(SM_CXSIZE);
2339 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2341 else
2343 GetWindowRect( menu->hWnd, &rect );
2344 if (menu->wFlags & MF_POPUP)
2346 RECT rc = item->rect;
2348 MENU_AdjustMenuItemRect(menu, &rc);
2350 /* The first item in the popup menu has to be at the
2351 same y position as the focused menu item */
2352 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2353 rect.top += rc.top - MENU_TOP_MARGIN;
2354 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2355 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2356 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2358 else
2360 rect.left += item->rect.left;
2361 rect.top += item->rect.bottom;
2362 rect.right = item->rect.right - item->rect.left;
2363 rect.bottom = item->rect.bottom - item->rect.top;
2367 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem,
2368 rect.left, rect.top, rect.right, rect.bottom );
2369 if (selectFirst)
2370 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2371 return item->hSubMenu;
2376 /**********************************************************************
2377 * MENU_IsMenuActive
2379 HWND MENU_IsMenuActive(void)
2381 return top_popup;
2384 /***********************************************************************
2385 * MENU_PtMenu
2387 * Walks menu chain trying to find a menu pt maps to.
2389 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2391 POPUPMENU *menu = MENU_GetMenu( hMenu );
2392 UINT item = menu->FocusedItem;
2393 HMENU ret;
2395 /* try subpopup first (if any) */
2396 ret = (item != NO_SELECTED_ITEM &&
2397 (menu->items[item].fType & MF_POPUP) &&
2398 (menu->items[item].fState & MF_MOUSESELECT))
2399 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2401 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2403 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2404 if( menu->wFlags & MF_POPUP )
2406 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2408 else if (ht == HTSYSMENU)
2409 ret = get_win_sys_menu( menu->hWnd );
2410 else if (ht == HTMENU)
2411 ret = GetMenu( menu->hWnd );
2413 return ret;
2416 /***********************************************************************
2417 * MENU_ExecFocusedItem
2419 * Execute a menu item (for instance when user pressed Enter).
2420 * Return the wID of the executed item. Otherwise, -1 indicating
2421 * that no menu item was executed, -2 if a popup is shown;
2422 * Have to receive the flags for the TrackPopupMenu options to avoid
2423 * sending unwanted message.
2426 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2428 MENUITEM *item;
2429 POPUPMENU *menu = MENU_GetMenu( hMenu );
2431 TRACE("%p hmenu=%p\n", pmt, hMenu);
2433 if (!menu || !menu->nItems ||
2434 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2436 item = &menu->items[menu->FocusedItem];
2438 TRACE("%p %08x %p\n", hMenu, item->wID, item->hSubMenu);
2440 if (!(item->fType & MF_POPUP))
2442 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2444 /* If TPM_RETURNCMD is set you return the id, but
2445 do not send a message to the owner */
2446 if(!(wFlags & TPM_RETURNCMD))
2448 if( menu->wFlags & MF_SYSMENU )
2449 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2450 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2451 else
2453 if (menu->dwStyle & MNS_NOTIFYBYPOS)
2454 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2455 (LPARAM)hMenu);
2456 else
2457 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2460 return item->wID;
2463 else
2465 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2466 return -2;
2469 return -1;
2472 /***********************************************************************
2473 * MENU_SwitchTracking
2475 * Helper function for menu navigation routines.
2477 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id )
2479 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2480 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2482 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2484 if( pmt->hTopMenu != hPtMenu &&
2485 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2487 /* both are top level menus (system and menu-bar) */
2488 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2489 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2490 pmt->hTopMenu = hPtMenu;
2492 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE );
2493 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2497 /***********************************************************************
2498 * MENU_ButtonDown
2500 * Return TRUE if we can go on with menu tracking.
2502 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2504 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2506 if (hPtMenu)
2508 UINT id = 0;
2509 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2510 MENUITEM *item;
2512 if( IS_SYSTEM_MENU(ptmenu) )
2513 item = ptmenu->items;
2514 else
2515 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2517 if( item )
2519 if( ptmenu->FocusedItem != id )
2520 MENU_SwitchTracking( pmt, hPtMenu, id );
2522 /* If the popup menu is not already "popped" */
2523 if(!(item->fState & MF_MOUSESELECT ))
2525 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2528 return TRUE;
2530 /* Else the click was on the menu bar, finish the tracking */
2532 return FALSE;
2535 /***********************************************************************
2536 * MENU_ButtonUp
2538 * Return the value of MENU_ExecFocusedItem if
2539 * the selected item was not a popup. Else open the popup.
2540 * A -1 return value indicates that we go on with menu tracking.
2543 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2545 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2547 if (hPtMenu)
2549 UINT id = 0;
2550 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2551 MENUITEM *item;
2553 if( IS_SYSTEM_MENU(ptmenu) )
2554 item = ptmenu->items;
2555 else
2556 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2558 if( item && (ptmenu->FocusedItem == id ))
2560 if( !(item->fType & MF_POPUP) )
2562 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2563 return (executedMenuId < 0) ? -1 : executedMenuId;
2566 /* If we are dealing with the top-level menu */
2567 /* and this is a click on an already "popped" item: */
2568 /* Stop the menu tracking and close the opened submenus */
2569 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2570 return 0;
2572 ptmenu->bTimeToHide = TRUE;
2574 return -1;
2578 /***********************************************************************
2579 * MENU_MouseMove
2581 * Return TRUE if we can go on with menu tracking.
2583 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2585 UINT id = NO_SELECTED_ITEM;
2586 POPUPMENU *ptmenu = NULL;
2588 if( hPtMenu )
2590 ptmenu = MENU_GetMenu( hPtMenu );
2591 if( IS_SYSTEM_MENU(ptmenu) )
2592 id = 0;
2593 else
2594 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2597 if( id == NO_SELECTED_ITEM )
2599 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2600 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2603 else if( ptmenu->FocusedItem != id )
2605 MENU_SwitchTracking( pmt, hPtMenu, id );
2606 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2608 return TRUE;
2612 /***********************************************************************
2613 * MENU_SetCapture
2615 static void MENU_SetCapture( HWND hwnd )
2617 HWND previous = 0;
2619 SERVER_START_REQ( set_capture_window )
2621 req->handle = hwnd;
2622 req->flags = CAPTURE_MENU;
2623 if (!wine_server_call_err( req ))
2625 previous = reply->previous;
2626 hwnd = reply->full_handle;
2629 SERVER_END_REQ;
2631 if (previous && previous != hwnd)
2632 SendMessageW( previous, WM_CAPTURECHANGED, 0, (LPARAM)hwnd );
2636 /***********************************************************************
2637 * MENU_DoNextMenu
2639 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2641 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk )
2643 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2645 if( (vk == VK_LEFT && menu->FocusedItem == 0 ) ||
2646 (vk == VK_RIGHT && menu->FocusedItem == menu->nItems - 1))
2648 MDINEXTMENU next_menu;
2649 HMENU hNewMenu;
2650 HWND hNewWnd;
2651 UINT id = 0;
2653 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2654 next_menu.hmenuNext = 0;
2655 next_menu.hwndNext = 0;
2656 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2658 TRACE("%p [%p] -> %p [%p]\n",
2659 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2661 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2663 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2664 hNewWnd = pmt->hOwnerWnd;
2665 if( IS_SYSTEM_MENU(menu) )
2667 /* switch to the menu bar */
2669 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2671 if( vk == VK_LEFT )
2673 menu = MENU_GetMenu( hNewMenu );
2674 id = menu->nItems - 1;
2677 else if (style & WS_SYSMENU )
2679 /* switch to the system menu */
2680 hNewMenu = get_win_sys_menu( hNewWnd );
2682 else return FALSE;
2684 else /* application returned a new menu to switch to */
2686 hNewMenu = next_menu.hmenuNext;
2687 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2689 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2691 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2693 if (style & WS_SYSMENU &&
2694 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2696 /* get the real system menu */
2697 hNewMenu = get_win_sys_menu(hNewWnd);
2699 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2701 /* FIXME: Not sure what to do here;
2702 * perhaps try to track hNewMenu as a popup? */
2704 TRACE(" -- got confused.\n");
2705 return FALSE;
2708 else return FALSE;
2711 if( hNewMenu != pmt->hTopMenu )
2713 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2714 FALSE, 0 );
2715 if( pmt->hCurrentMenu != pmt->hTopMenu )
2716 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2719 if( hNewWnd != pmt->hOwnerWnd )
2721 pmt->hOwnerWnd = hNewWnd;
2722 MENU_SetCapture( pmt->hOwnerWnd );
2725 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2726 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2728 return TRUE;
2730 return FALSE;
2733 /***********************************************************************
2734 * MENU_SuspendPopup
2736 * The idea is not to show the popup if the next input message is
2737 * going to hide it anyway.
2739 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2741 MSG msg;
2743 msg.hwnd = pmt->hOwnerWnd;
2745 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2746 pmt->trackFlags |= TF_SKIPREMOVE;
2748 switch( uMsg )
2750 case WM_KEYDOWN:
2751 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2752 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2754 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2755 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2756 if( msg.message == WM_KEYDOWN &&
2757 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2759 pmt->trackFlags |= TF_SUSPENDPOPUP;
2760 return TRUE;
2763 break;
2766 /* failures go through this */
2767 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2768 return FALSE;
2771 /***********************************************************************
2772 * MENU_KeyEscape
2774 * Handle a VK_ESCAPE key event in a menu.
2776 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2778 BOOL bEndMenu = TRUE;
2780 if (pmt->hCurrentMenu != pmt->hTopMenu)
2782 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2784 if (menu->wFlags & MF_POPUP)
2786 HMENU hmenutmp, hmenuprev;
2788 hmenuprev = hmenutmp = pmt->hTopMenu;
2790 /* close topmost popup */
2791 while (hmenutmp != pmt->hCurrentMenu)
2793 hmenuprev = hmenutmp;
2794 hmenutmp = MENU_GetSubPopup( hmenuprev );
2797 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2798 pmt->hCurrentMenu = hmenuprev;
2799 bEndMenu = FALSE;
2803 return bEndMenu;
2806 /***********************************************************************
2807 * MENU_KeyLeft
2809 * Handle a VK_LEFT key event in a menu.
2811 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2813 POPUPMENU *menu;
2814 HMENU hmenutmp, hmenuprev;
2815 UINT prevcol;
2817 hmenuprev = hmenutmp = pmt->hTopMenu;
2818 menu = MENU_GetMenu( hmenutmp );
2820 /* Try to move 1 column left (if possible) */
2821 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2822 NO_SELECTED_ITEM ) {
2824 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2825 prevcol, TRUE, 0 );
2826 return;
2829 /* close topmost popup */
2830 while (hmenutmp != pmt->hCurrentMenu)
2832 hmenuprev = hmenutmp;
2833 hmenutmp = MENU_GetSubPopup( hmenuprev );
2836 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE );
2837 pmt->hCurrentMenu = hmenuprev;
2839 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2841 /* move menu bar selection if no more popups are left */
2843 if( !MENU_DoNextMenu( pmt, VK_LEFT) )
2844 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2846 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2848 /* A sublevel menu was displayed - display the next one
2849 * unless there is another displacement coming up */
2851 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2852 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2853 pmt->hTopMenu, TRUE, wFlags);
2859 /***********************************************************************
2860 * MENU_KeyRight
2862 * Handle a VK_RIGHT key event in a menu.
2864 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2866 HMENU hmenutmp;
2867 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2868 UINT nextcol;
2870 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2871 pmt->hCurrentMenu,
2872 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2873 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2875 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2877 /* If already displaying a popup, try to display sub-popup */
2879 hmenutmp = pmt->hCurrentMenu;
2880 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2882 /* if subpopup was displayed then we are done */
2883 if (hmenutmp != pmt->hCurrentMenu) return;
2886 /* Check to see if there's another column */
2887 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2888 NO_SELECTED_ITEM ) {
2889 TRACE("Going to %d.\n", nextcol );
2890 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2891 nextcol, TRUE, 0 );
2892 return;
2895 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2897 if( pmt->hCurrentMenu != pmt->hTopMenu )
2899 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE );
2900 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2901 } else hmenutmp = 0;
2903 /* try to move to the next item */
2904 if( !MENU_DoNextMenu( pmt, VK_RIGHT) )
2905 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2907 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2908 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2909 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2910 pmt->hTopMenu, TRUE, wFlags);
2914 /***********************************************************************
2915 * MENU_TrackMenu
2917 * Menu tracking code.
2919 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2920 HWND hwnd, const RECT *lprect )
2922 MSG msg;
2923 POPUPMENU *menu;
2924 BOOL fRemove;
2925 INT executedMenuId = -1;
2926 MTRACKER mt;
2927 BOOL enterIdleSent = FALSE;
2929 mt.trackFlags = 0;
2930 mt.hCurrentMenu = hmenu;
2931 mt.hTopMenu = hmenu;
2932 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
2933 mt.pt.x = x;
2934 mt.pt.y = y;
2936 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2937 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
2939 fEndMenu = FALSE;
2940 if (!(menu = MENU_GetMenu( hmenu )))
2942 WARN("Invalid menu handle %p\n", hmenu);
2943 SetLastError(ERROR_INVALID_MENU_HANDLE);
2944 return FALSE;
2947 if (wFlags & TPM_BUTTONDOWN)
2949 /* Get the result in order to start the tracking or not */
2950 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
2951 fEndMenu = !fRemove;
2954 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
2956 MENU_SetCapture( mt.hOwnerWnd );
2958 while (!fEndMenu)
2960 menu = MENU_GetMenu( mt.hCurrentMenu );
2961 if (!menu) /* sometimes happens if I do a window manager close */
2962 break;
2964 /* we have to keep the message in the queue until it's
2965 * clear that menu loop is not over yet. */
2967 for (;;)
2969 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
2971 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
2972 /* remove the message from the queue */
2973 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2975 else
2977 if (!enterIdleSent)
2979 HWND win = (wFlags & TPM_ENTERIDLEEX && menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
2980 enterIdleSent = TRUE;
2981 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
2983 WaitMessage();
2987 /* check if EndMenu() tried to cancel us, by posting this message */
2988 if(msg.message == WM_CANCELMODE)
2990 /* we are now out of the loop */
2991 fEndMenu = TRUE;
2993 /* remove the message from the queue */
2994 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
2996 /* break out of internal loop, ala ESCAPE */
2997 break;
3000 TranslateMessage( &msg );
3001 mt.pt = msg.pt;
3003 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3004 enterIdleSent=FALSE;
3006 fRemove = FALSE;
3007 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3010 * Use the mouse coordinates in lParam instead of those in the MSG
3011 * struct to properly handle synthetic messages. They are already
3012 * in screen coordinates.
3014 mt.pt.x = (short)LOWORD(msg.lParam);
3015 mt.pt.y = (short)HIWORD(msg.lParam);
3017 /* Find a menu for this mouse event */
3018 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3020 switch(msg.message)
3022 /* no WM_NC... messages in captured state */
3024 case WM_RBUTTONDBLCLK:
3025 case WM_RBUTTONDOWN:
3026 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3027 /* fall through */
3028 case WM_LBUTTONDBLCLK:
3029 case WM_LBUTTONDOWN:
3030 /* If the message belongs to the menu, removes it from the queue */
3031 /* Else, end menu tracking */
3032 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3033 fEndMenu = !fRemove;
3034 break;
3036 case WM_RBUTTONUP:
3037 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3038 /* fall through */
3039 case WM_LBUTTONUP:
3040 /* Check if a menu was selected by the mouse */
3041 if (hmenu)
3043 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3045 /* End the loop if executedMenuId is an item ID */
3046 /* or if the job was done (executedMenuId = 0). */
3047 fEndMenu = fRemove = (executedMenuId != -1);
3049 /* No menu was selected by the mouse */
3050 /* if the function was called by TrackPopupMenu, continue
3051 with the menu tracking. If not, stop it */
3052 else
3053 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3055 break;
3057 case WM_MOUSEMOVE:
3058 /* the selected menu item must be changed every time */
3059 /* the mouse moves. */
3061 if (hmenu)
3062 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3064 } /* switch(msg.message) - mouse */
3066 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3068 fRemove = TRUE; /* Keyboard messages are always removed */
3069 switch(msg.message)
3071 case WM_KEYDOWN:
3072 case WM_SYSKEYDOWN:
3073 switch(msg.wParam)
3075 case VK_MENU:
3076 fEndMenu = TRUE;
3077 break;
3079 case VK_HOME:
3080 case VK_END:
3081 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3082 NO_SELECTED_ITEM, FALSE, 0 );
3083 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3084 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3085 break;
3087 case VK_UP:
3088 case VK_DOWN: /* If on menu bar, pull-down the menu */
3090 menu = MENU_GetMenu( mt.hCurrentMenu );
3091 if (!(menu->wFlags & MF_POPUP))
3092 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3093 else /* otherwise try to move selection */
3094 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3095 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3096 break;
3098 case VK_LEFT:
3099 MENU_KeyLeft( &mt, wFlags );
3100 break;
3102 case VK_RIGHT:
3103 MENU_KeyRight( &mt, wFlags );
3104 break;
3106 case VK_ESCAPE:
3107 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3108 break;
3110 case VK_F1:
3112 HELPINFO hi;
3113 hi.cbSize = sizeof(HELPINFO);
3114 hi.iContextType = HELPINFO_MENUITEM;
3115 if (menu->FocusedItem == NO_SELECTED_ITEM)
3116 hi.iCtrlId = 0;
3117 else
3118 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3119 hi.hItemHandle = hmenu;
3120 hi.dwContextId = menu->dwContextHelpID;
3121 hi.MousePos = msg.pt;
3122 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3123 break;
3126 default:
3127 break;
3129 break; /* WM_KEYDOWN */
3131 case WM_CHAR:
3132 case WM_SYSCHAR:
3134 UINT pos;
3136 if (msg.wParam == '\r' || msg.wParam == ' ')
3138 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3139 fEndMenu = (executedMenuId != -2);
3141 break;
3144 /* Hack to avoid control chars. */
3145 /* We will find a better way real soon... */
3146 if (msg.wParam < 32) break;
3148 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3149 LOWORD(msg.wParam), FALSE );
3150 if (pos == (UINT)-2) fEndMenu = TRUE;
3151 else if (pos == (UINT)-1) MessageBeep(0);
3152 else
3154 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3155 TRUE, 0 );
3156 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3157 fEndMenu = (executedMenuId != -2);
3160 break;
3161 } /* switch(msg.message) - kbd */
3163 else
3165 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3166 DispatchMessageW( &msg );
3167 continue;
3170 if (!fEndMenu) fRemove = TRUE;
3172 /* finally remove message from the queue */
3174 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3175 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3176 else mt.trackFlags &= ~TF_SKIPREMOVE;
3179 MENU_SetCapture(0); /* release the capture */
3181 /* If dropdown is still painted and the close box is clicked on
3182 then the menu will be destroyed as part of the DispatchMessage above.
3183 This will then invalidate the menu handle in mt.hTopMenu. We should
3184 check for this first. */
3185 if( IsMenu( mt.hTopMenu ) )
3187 menu = MENU_GetMenu( mt.hTopMenu );
3189 if( IsWindow( mt.hOwnerWnd ) )
3191 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE );
3193 if (menu && (menu->wFlags & MF_POPUP))
3195 DestroyWindow( menu->hWnd );
3196 menu->hWnd = 0;
3198 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3199 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKELONG(0,0xffff), 0 );
3202 /* Reset the variable for hiding menu */
3203 if( menu ) menu->bTimeToHide = FALSE;
3206 /* The return value is only used by TrackPopupMenu */
3207 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3208 if (executedMenuId < 0) executedMenuId = 0;
3209 return executedMenuId;
3212 /***********************************************************************
3213 * MENU_InitTracking
3215 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3217 POPUPMENU *menu;
3219 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3221 HideCaret(0);
3223 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3224 if (!(wFlags & TPM_NONOTIFY))
3225 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3227 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3229 if (!(wFlags & TPM_NONOTIFY))
3231 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3232 /* If an app changed/recreated menu bar entries in WM_INITMENU
3233 * menu sizes will be recalculated once the menu created/shown.
3237 /* This makes the menus of applications built with Delphi work.
3238 * It also enables menus to be displayed in more than one window,
3239 * but there are some bugs left that need to be fixed in this case.
3241 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3243 return TRUE;
3245 /***********************************************************************
3246 * MENU_ExitTracking
3248 static BOOL MENU_ExitTracking(HWND hWnd)
3250 TRACE("hwnd=%p\n", hWnd);
3252 SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 );
3253 ShowCaret(0);
3254 top_popup = 0;
3255 return TRUE;
3258 /***********************************************************************
3259 * MENU_TrackMouseMenuBar
3261 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3263 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3265 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3266 UINT wFlags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3268 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3270 if (IsMenu(hMenu))
3272 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3273 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3274 MENU_ExitTracking(hWnd);
3279 /***********************************************************************
3280 * MENU_TrackKbdMenuBar
3282 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3284 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3286 UINT uItem = NO_SELECTED_ITEM;
3287 HMENU hTrackMenu;
3288 UINT wFlags = TPM_ENTERIDLEEX | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3290 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3292 /* find window that has a menu */
3294 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3295 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3297 /* check if we have to track a system menu */
3299 hTrackMenu = GetMenu( hwnd );
3300 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3302 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3303 hTrackMenu = get_win_sys_menu( hwnd );
3304 uItem = 0;
3305 wParam |= HTSYSMENU; /* prevent item lookup */
3308 if (!IsMenu( hTrackMenu )) return;
3310 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3312 if( wChar && wChar != ' ' )
3314 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3315 if ( uItem >= (UINT)(-2) )
3317 if( uItem == (UINT)(-1) ) MessageBeep(0);
3318 /* schedule end of menu tracking */
3319 wFlags |= TF_ENDMENU;
3320 goto track_menu;
3324 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3326 if (wParam & HTSYSMENU)
3328 /* prevent sysmenu activation for managed windows on Alt down/up */
3329 if (GetPropA( hwnd, "__wine_x11_managed" ))
3330 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3332 else
3334 if( uItem == NO_SELECTED_ITEM )
3335 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3336 else
3337 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3340 track_menu:
3341 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3342 MENU_ExitTracking( hwnd );
3346 /**********************************************************************
3347 * TrackPopupMenu (USER32.@)
3349 * Like the win32 API, the function return the command ID only if the
3350 * flag TPM_RETURNCMD is on.
3353 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3354 INT nReserved, HWND hWnd, const RECT *lpRect )
3356 BOOL ret = FALSE;
3358 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3360 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3361 if (!(wFlags & TPM_NONOTIFY))
3362 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3364 if (MENU_ShowPopup( hWnd, hMenu, 0, x, y, 0, 0 ))
3365 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, lpRect );
3366 MENU_ExitTracking(hWnd);
3368 return ret;
3371 /**********************************************************************
3372 * TrackPopupMenuEx (USER32.@)
3374 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3375 HWND hWnd, LPTPMPARAMS lpTpm )
3377 FIXME("not fully implemented\n" );
3378 return TrackPopupMenu( hMenu, wFlags, x, y, 0, hWnd,
3379 lpTpm ? &lpTpm->rcExclude : NULL );
3382 /***********************************************************************
3383 * PopupMenuWndProc
3385 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3387 static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3389 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd, message, wParam, lParam);
3391 switch(message)
3393 case WM_CREATE:
3395 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3396 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3397 return 0;
3400 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3401 return MA_NOACTIVATE;
3403 case WM_PAINT:
3405 PAINTSTRUCT ps;
3406 BeginPaint( hwnd, &ps );
3407 MENU_DrawPopupMenu( hwnd, ps.hdc,
3408 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3409 EndPaint( hwnd, &ps );
3410 return 0;
3412 case WM_ERASEBKGND:
3413 return 1;
3415 case WM_DESTROY:
3416 /* zero out global pointer in case resident popup window was destroyed. */
3417 if (hwnd == top_popup) top_popup = 0;
3418 break;
3420 case WM_SHOWWINDOW:
3422 if( wParam )
3424 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3426 else
3427 SetWindowLongPtrW( hwnd, 0, 0 );
3428 break;
3430 case MM_SETMENUHANDLE:
3431 SetWindowLongPtrW( hwnd, 0, wParam );
3432 break;
3434 case MM_GETMENUHANDLE:
3435 return GetWindowLongPtrW( hwnd, 0 );
3437 default:
3438 return DefWindowProcW( hwnd, message, wParam, lParam );
3440 return 0;
3444 /***********************************************************************
3445 * MENU_GetMenuBarHeight
3447 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3449 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3450 INT orgX, INT orgY )
3452 HDC hdc;
3453 RECT rectBar;
3454 LPPOPUPMENU lppop;
3456 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3458 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3460 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3461 SelectObject( hdc, get_menu_font(FALSE));
3462 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3463 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3464 ReleaseDC( hwnd, hdc );
3465 return lppop->Height;
3469 /*******************************************************************
3470 * ChangeMenuA (USER32.@)
3472 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3473 UINT id, UINT flags )
3475 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3476 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3477 id, data );
3478 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3479 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3480 id, data );
3481 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3482 flags & MF_BYPOSITION ? pos : id,
3483 flags & ~MF_REMOVE );
3484 /* Default: MF_INSERT */
3485 return InsertMenuA( hMenu, pos, flags, id, data );
3489 /*******************************************************************
3490 * ChangeMenuW (USER32.@)
3492 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3493 UINT id, UINT flags )
3495 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3496 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3497 id, data );
3498 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3499 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3500 id, data );
3501 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3502 flags & MF_BYPOSITION ? pos : id,
3503 flags & ~MF_REMOVE );
3504 /* Default: MF_INSERT */
3505 return InsertMenuW( hMenu, pos, flags, id, data );
3509 /*******************************************************************
3510 * CheckMenuItem (USER32.@)
3512 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3514 MENUITEM *item;
3515 DWORD ret;
3517 TRACE("menu=%p id=%04x flags=%04x\n", hMenu, id, flags );
3518 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3519 ret = item->fState & MF_CHECKED;
3520 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3521 else item->fState &= ~MF_CHECKED;
3522 return ret;
3526 /**********************************************************************
3527 * EnableMenuItem (USER32.@)
3529 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3531 UINT oldflags;
3532 MENUITEM *item;
3533 POPUPMENU *menu;
3535 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3537 /* Get the Popupmenu to access the owner menu */
3538 if (!(menu = MENU_GetMenu(hMenu)))
3539 return (UINT)-1;
3541 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3542 return (UINT)-1;
3544 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3545 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3547 /* If the close item in the system menu change update the close button */
3548 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3550 if (menu->hSysMenuOwner != 0)
3552 RECT rc;
3553 POPUPMENU* parentMenu;
3555 /* Get the parent menu to access*/
3556 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3557 return (UINT)-1;
3559 /* Refresh the frame to reflect the change */
3560 GetWindowRect(parentMenu->hWnd, &rc);
3561 MapWindowPoints(0, parentMenu->hWnd, (POINT *)&rc, 2);
3562 rc.bottom = 0;
3563 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3567 return oldflags;
3571 /*******************************************************************
3572 * GetMenuStringA (USER32.@)
3574 INT WINAPI GetMenuStringA(
3575 HMENU hMenu, /* [in] menuhandle */
3576 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3577 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3578 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3579 UINT wFlags /* [in] MF_ flags */
3581 MENUITEM *item;
3583 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3584 if (str && nMaxSiz) str[0] = '\0';
3585 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3586 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3587 return 0;
3589 if (!item->text) return 0;
3590 if (!str || !nMaxSiz) return strlenW(item->text);
3591 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3592 str[nMaxSiz-1] = 0;
3593 TRACE("returning '%s'\n", str );
3594 return strlen(str);
3598 /*******************************************************************
3599 * GetMenuStringW (USER32.@)
3601 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3602 LPWSTR str, INT nMaxSiz, UINT wFlags )
3604 MENUITEM *item;
3606 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3607 if (str && nMaxSiz) str[0] = '\0';
3608 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3609 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3610 return 0;
3612 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3613 if( !(item->text)) {
3614 str[0] = 0;
3615 return 0;
3617 lstrcpynW( str, item->text, nMaxSiz );
3618 return strlenW(str);
3622 /**********************************************************************
3623 * HiliteMenuItem (USER32.@)
3625 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3626 UINT wHilite )
3628 LPPOPUPMENU menu;
3629 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3630 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3631 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3632 if (menu->FocusedItem == wItemID) return TRUE;
3633 MENU_HideSubPopups( hWnd, hMenu, FALSE );
3634 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3635 return TRUE;
3639 /**********************************************************************
3640 * GetMenuState (USER32.@)
3642 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3644 MENUITEM *item;
3645 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3646 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3647 debug_print_menuitem (" item: ", item, "");
3648 if (item->fType & MF_POPUP)
3650 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3651 if (!menu) return -1;
3652 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3654 else
3656 /* We used to (from way back then) mask the result to 0xff. */
3657 /* I don't know why and it seems wrong as the documented */
3658 /* return flag MF_SEPARATOR is outside that mask. */
3659 return (item->fType | item->fState);
3664 /**********************************************************************
3665 * GetMenuItemCount (USER32.@)
3667 INT WINAPI GetMenuItemCount( HMENU hMenu )
3669 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3670 if (!menu) return -1;
3671 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3672 return menu->nItems;
3676 /**********************************************************************
3677 * GetMenuItemID (USER32.@)
3679 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3681 MENUITEM * lpmi;
3683 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3684 if (lpmi->fType & MF_POPUP) return -1;
3685 return lpmi->wID;
3690 /*******************************************************************
3691 * InsertMenuW (USER32.@)
3693 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3694 UINT_PTR id, LPCWSTR str )
3696 MENUITEM *item;
3698 if (IS_STRING_ITEM(flags) && str)
3699 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3700 hMenu, pos, flags, id, debugstr_w(str) );
3701 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3702 hMenu, pos, flags, id, str );
3704 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3706 if (!(MENU_SetItemData( item, flags, id, str )))
3708 RemoveMenu( hMenu, pos, flags );
3709 return FALSE;
3712 if (flags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
3713 (MENU_GetMenu((HMENU)id))->wFlags |= MF_POPUP;
3715 item->hCheckBit = item->hUnCheckBit = 0;
3716 return TRUE;
3720 /*******************************************************************
3721 * InsertMenuA (USER32.@)
3723 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3724 UINT_PTR id, LPCSTR str )
3726 BOOL ret = FALSE;
3728 if (IS_STRING_ITEM(flags) && str)
3730 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3731 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3732 if (newstr)
3734 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3735 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3736 HeapFree( GetProcessHeap(), 0, newstr );
3738 return ret;
3740 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3744 /*******************************************************************
3745 * AppendMenuA (USER32.@)
3747 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3748 UINT_PTR id, LPCSTR data )
3750 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3754 /*******************************************************************
3755 * AppendMenuW (USER32.@)
3757 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3758 UINT_PTR id, LPCWSTR data )
3760 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3764 /**********************************************************************
3765 * RemoveMenu (USER32.@)
3767 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3769 LPPOPUPMENU menu;
3770 MENUITEM *item;
3772 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3773 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3774 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3776 /* Remove item */
3778 MENU_FreeItemData( item );
3780 if (--menu->nItems == 0)
3782 HeapFree( GetProcessHeap(), 0, menu->items );
3783 menu->items = NULL;
3785 else
3787 while(nPos < menu->nItems)
3789 *item = *(item+1);
3790 item++;
3791 nPos++;
3793 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3794 menu->nItems * sizeof(MENUITEM) );
3796 return TRUE;
3800 /**********************************************************************
3801 * DeleteMenu (USER32.@)
3803 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3805 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3806 if (!item) return FALSE;
3807 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3808 /* nPos is now the position of the item */
3809 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3810 return TRUE;
3814 /*******************************************************************
3815 * ModifyMenuW (USER32.@)
3817 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3818 UINT_PTR id, LPCWSTR str )
3820 MENUITEM *item;
3822 if (IS_STRING_ITEM(flags))
3823 TRACE("%p %d %04x %04x %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3824 else
3825 TRACE("%p %d %04x %04x %p\n", hMenu, pos, flags, id, str );
3827 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3828 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3829 return MENU_SetItemData( item, flags, id, str );
3833 /*******************************************************************
3834 * ModifyMenuA (USER32.@)
3836 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3837 UINT_PTR id, LPCSTR str )
3839 BOOL ret = FALSE;
3841 if (IS_STRING_ITEM(flags) && str)
3843 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3844 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3845 if (newstr)
3847 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3848 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3849 HeapFree( GetProcessHeap(), 0, newstr );
3851 return ret;
3853 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3857 /**********************************************************************
3858 * CreatePopupMenu (USER32.@)
3860 HMENU WINAPI CreatePopupMenu(void)
3862 HMENU hmenu;
3863 POPUPMENU *menu;
3865 if (!(hmenu = CreateMenu())) return 0;
3866 menu = MENU_GetMenu( hmenu );
3867 menu->wFlags |= MF_POPUP;
3868 menu->bTimeToHide = FALSE;
3869 return hmenu;
3873 /**********************************************************************
3874 * GetMenuCheckMarkDimensions (USER.417)
3875 * GetMenuCheckMarkDimensions (USER32.@)
3877 DWORD WINAPI GetMenuCheckMarkDimensions(void)
3879 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
3883 /**********************************************************************
3884 * SetMenuItemBitmaps (USER32.@)
3886 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
3887 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
3889 MENUITEM *item;
3890 TRACE("(%p, %04x, %04x, %p, %p)\n",
3891 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
3892 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3894 if (!hNewCheck && !hNewUnCheck)
3896 item->fState &= ~MF_USECHECKBITMAPS;
3898 else /* Install new bitmaps */
3900 item->hCheckBit = hNewCheck;
3901 item->hUnCheckBit = hNewUnCheck;
3902 item->fState |= MF_USECHECKBITMAPS;
3904 return TRUE;
3908 /**********************************************************************
3909 * CreateMenu (USER32.@)
3911 HMENU WINAPI CreateMenu(void)
3913 HMENU hMenu;
3914 LPPOPUPMENU menu;
3915 if (!(hMenu = USER_HEAP_ALLOC( sizeof(POPUPMENU) ))) return 0;
3916 menu = (LPPOPUPMENU) USER_HEAP_LIN_ADDR(hMenu);
3918 ZeroMemory(menu, sizeof(POPUPMENU));
3919 menu->wMagic = MENU_MAGIC;
3920 menu->FocusedItem = NO_SELECTED_ITEM;
3921 menu->bTimeToHide = FALSE;
3923 TRACE("return %p\n", hMenu );
3925 return hMenu;
3929 /**********************************************************************
3930 * DestroyMenu (USER32.@)
3932 BOOL WINAPI DestroyMenu( HMENU hMenu )
3934 LPPOPUPMENU lppop = MENU_GetMenu(hMenu);
3936 TRACE("(%p)\n", hMenu);
3939 if (!lppop) return FALSE;
3941 lppop->wMagic = 0; /* Mark it as destroyed */
3943 /* DestroyMenu should not destroy system menu popup owner */
3944 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
3946 DestroyWindow( lppop->hWnd );
3947 lppop->hWnd = 0;
3950 if (lppop->items) /* recursively destroy submenus */
3952 int i;
3953 MENUITEM *item = lppop->items;
3954 for (i = lppop->nItems; i > 0; i--, item++)
3956 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
3957 MENU_FreeItemData( item );
3959 HeapFree( GetProcessHeap(), 0, lppop->items );
3961 USER_HEAP_FREE( hMenu );
3962 return TRUE;
3966 /**********************************************************************
3967 * GetSystemMenu (USER32.@)
3969 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
3971 WND *wndPtr = WIN_GetPtr( hWnd );
3972 HMENU retvalue = 0;
3974 if (wndPtr == WND_DESKTOP) return 0;
3975 if (wndPtr == WND_OTHER_PROCESS)
3977 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
3979 else if (wndPtr)
3981 if (wndPtr->hSysMenu && bRevert)
3983 DestroyMenu(wndPtr->hSysMenu);
3984 wndPtr->hSysMenu = 0;
3987 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
3988 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
3990 if( wndPtr->hSysMenu )
3992 POPUPMENU *menu;
3993 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
3995 /* Store the dummy sysmenu handle to facilitate the refresh */
3996 /* of the close button if the SC_CLOSE item change */
3997 menu = MENU_GetMenu(retvalue);
3998 if ( menu )
3999 menu->hSysMenuOwner = wndPtr->hSysMenu;
4001 WIN_ReleasePtr( wndPtr );
4003 return bRevert ? 0 : retvalue;
4007 /*******************************************************************
4008 * SetSystemMenu (USER32.@)
4010 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4012 WND *wndPtr = WIN_GetPtr( hwnd );
4014 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4016 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4017 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4018 WIN_ReleasePtr( wndPtr );
4019 return TRUE;
4021 return FALSE;
4025 /**********************************************************************
4026 * GetMenu (USER32.@)
4028 HMENU WINAPI GetMenu( HWND hWnd )
4030 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4031 TRACE("for %p returning %p\n", hWnd, retvalue);
4032 return retvalue;
4035 /**********************************************************************
4036 * GetMenuBarInfo (USER32.@)
4038 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4040 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
4041 return FALSE;
4044 /**********************************************************************
4045 * MENU_SetMenu
4047 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4048 * SetWindowPos call that would result if SetMenu were called directly.
4050 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4052 TRACE("(%p, %p);\n", hWnd, hMenu);
4054 if (hMenu && !IsMenu(hMenu))
4056 WARN("hMenu %p is not a menu handle\n", hMenu);
4057 return FALSE;
4059 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4060 return FALSE;
4062 hWnd = WIN_GetFullHandle( hWnd );
4063 if (GetCapture() == hWnd) MENU_SetCapture(0); /* release the capture */
4065 if (hMenu != 0)
4067 LPPOPUPMENU lpmenu;
4069 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4071 lpmenu->hWnd = hWnd;
4072 lpmenu->Height = 0; /* Make sure we recalculate the size */
4074 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4075 return TRUE;
4079 /**********************************************************************
4080 * SetMenu (USER32.@)
4082 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4084 if(!MENU_SetMenu(hWnd, hMenu))
4085 return FALSE;
4087 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4088 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4089 return TRUE;
4093 /**********************************************************************
4094 * GetSubMenu (USER32.@)
4096 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4098 MENUITEM * lpmi;
4100 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4101 if (!(lpmi->fType & MF_POPUP)) return 0;
4102 return lpmi->hSubMenu;
4106 /**********************************************************************
4107 * DrawMenuBar (USER32.@)
4109 BOOL WINAPI DrawMenuBar( HWND hWnd )
4111 LPPOPUPMENU lppop;
4112 HMENU hMenu = GetMenu(hWnd);
4114 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4115 return FALSE;
4116 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4118 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4119 lppop->hwndOwner = hWnd;
4120 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4121 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4122 return TRUE;
4125 /***********************************************************************
4126 * DrawMenuBarTemp (USER32.@)
4128 * UNDOCUMENTED !!
4130 * called by W98SE desk.cpl Control Panel Applet
4132 * Not 100% sure about the param names, but close.
4134 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4136 LPPOPUPMENU lppop;
4137 UINT i,retvalue;
4138 HFONT hfontOld = 0;
4139 BOOL flat_menu = FALSE;
4141 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4143 if (!hMenu)
4144 hMenu = GetMenu(hwnd);
4146 if (!hFont)
4147 hFont = get_menu_font(FALSE);
4149 lppop = MENU_GetMenu( hMenu );
4150 if (lppop == NULL || lprect == NULL)
4152 retvalue = GetSystemMetrics(SM_CYMENU);
4153 goto END;
4156 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4158 hfontOld = SelectObject( hDC, hFont);
4160 if (lppop->Height == 0)
4161 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4163 lprect->bottom = lprect->top + lppop->Height;
4165 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4167 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4168 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4169 LineTo( hDC, lprect->right, lprect->bottom );
4171 if (lppop->nItems == 0)
4173 retvalue = GetSystemMetrics(SM_CYMENU);
4174 goto END;
4177 for (i = 0; i < lppop->nItems; i++)
4179 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4180 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4182 retvalue = lppop->Height;
4184 END:
4185 if (hfontOld) SelectObject (hDC, hfontOld);
4186 return retvalue;
4189 /***********************************************************************
4190 * EndMenu (USER.187)
4191 * EndMenu (USER32.@)
4193 void WINAPI EndMenu(void)
4195 /* if we are in the menu code, and it is active */
4196 if (!fEndMenu && top_popup)
4198 /* terminate the menu handling code */
4199 fEndMenu = TRUE;
4201 /* needs to be posted to wakeup the internal menu handler */
4202 /* which will now terminate the menu, in the event that */
4203 /* the main window was minimized, or lost focus, so we */
4204 /* don't end up with an orphaned menu */
4205 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4210 /***********************************************************************
4211 * LookupMenuHandle (USER.217)
4213 HMENU16 WINAPI LookupMenuHandle16( HMENU16 hmenu, INT16 id )
4215 HMENU hmenu32 = HMENU_32(hmenu);
4216 UINT id32 = id;
4217 if (!MENU_FindItem( &hmenu32, &id32, MF_BYCOMMAND )) return 0;
4218 else return HMENU_16(hmenu32);
4222 /**********************************************************************
4223 * LoadMenu (USER.150)
4225 HMENU16 WINAPI LoadMenu16( HINSTANCE16 instance, LPCSTR name )
4227 HRSRC16 hRsrc;
4228 HGLOBAL16 handle;
4229 HMENU16 hMenu;
4231 if (HIWORD(name) && name[0] == '#') name = (LPCSTR)atoi( name + 1 );
4232 if (!name) return 0;
4234 instance = GetExePtr( instance );
4235 if (!(hRsrc = FindResource16( instance, name, (LPSTR)RT_MENU ))) return 0;
4236 if (!(handle = LoadResource16( instance, hRsrc ))) return 0;
4237 hMenu = LoadMenuIndirect16(LockResource16(handle));
4238 FreeResource16( handle );
4239 return hMenu;
4243 /*****************************************************************
4244 * LoadMenuA (USER32.@)
4246 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4248 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4249 if (!hrsrc) return 0;
4250 return LoadMenuIndirectA( (LPCVOID)LoadResource( instance, hrsrc ));
4254 /*****************************************************************
4255 * LoadMenuW (USER32.@)
4257 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4259 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4260 if (!hrsrc) return 0;
4261 return LoadMenuIndirectW( (LPCVOID)LoadResource( instance, hrsrc ));
4265 /**********************************************************************
4266 * LoadMenuIndirect (USER.220)
4268 HMENU16 WINAPI LoadMenuIndirect16( LPCVOID template )
4270 HMENU hMenu;
4271 WORD version, offset;
4272 LPCSTR p = (LPCSTR)template;
4274 TRACE("(%p)\n", template );
4275 version = GET_WORD(p);
4276 p += sizeof(WORD);
4277 if (version)
4279 WARN("version must be 0 for Win16\n" );
4280 return 0;
4282 offset = GET_WORD(p);
4283 p += sizeof(WORD) + offset;
4284 if (!(hMenu = CreateMenu())) return 0;
4285 if (!MENU_ParseResource( p, hMenu, FALSE ))
4287 DestroyMenu( hMenu );
4288 return 0;
4290 return HMENU_16(hMenu);
4294 /**********************************************************************
4295 * LoadMenuIndirectW (USER32.@)
4297 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4299 HMENU hMenu;
4300 WORD version, offset;
4301 LPCSTR p = (LPCSTR)template;
4303 version = GET_WORD(p);
4304 p += sizeof(WORD);
4305 TRACE("%p, ver %d\n", template, version );
4306 switch (version)
4308 case 0: /* standard format is version of 0 */
4309 offset = GET_WORD(p);
4310 p += sizeof(WORD) + offset;
4311 if (!(hMenu = CreateMenu())) return 0;
4312 if (!MENU_ParseResource( p, hMenu, TRUE ))
4314 DestroyMenu( hMenu );
4315 return 0;
4317 return hMenu;
4318 case 1: /* extended format is version of 1 */
4319 offset = GET_WORD(p);
4320 p += sizeof(WORD) + offset;
4321 if (!(hMenu = CreateMenu())) return 0;
4322 if (!MENUEX_ParseResource( p, hMenu))
4324 DestroyMenu( hMenu );
4325 return 0;
4327 return hMenu;
4328 default:
4329 ERR("version %d not supported.\n", version);
4330 return 0;
4335 /**********************************************************************
4336 * LoadMenuIndirectA (USER32.@)
4338 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4340 return LoadMenuIndirectW( template );
4344 /**********************************************************************
4345 * IsMenu (USER32.@)
4347 BOOL WINAPI IsMenu(HMENU hmenu)
4349 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4351 if (!menu)
4353 SetLastError(ERROR_INVALID_MENU_HANDLE);
4354 return FALSE;
4356 return TRUE;
4359 /**********************************************************************
4360 * GetMenuItemInfo_common
4363 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4364 LPMENUITEMINFOW lpmii, BOOL unicode)
4366 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4368 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4370 if (!menu)
4371 return FALSE;
4373 if( lpmii->fMask & MIIM_TYPE) {
4374 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4375 WARN("invalid combination of fMask bits used\n");
4376 /* this does not happen on Win9x/ME */
4377 SetLastError( ERROR_INVALID_PARAMETER);
4378 return FALSE;
4380 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4381 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4382 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4383 if( lpmii->fType & MFT_BITMAP) {
4384 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4385 lpmii->cch = 0;
4386 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4387 /* this does not happen on Win9x/ME */
4388 lpmii->dwTypeData = 0;
4389 lpmii->cch = 0;
4393 /* copy the text string */
4394 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4395 if( !menu->text ) {
4396 if(lpmii->dwTypeData && lpmii->cch) {
4397 lpmii->cch = 0;
4398 if( unicode)
4399 *((WCHAR *)lpmii->dwTypeData) = 0;
4400 else
4401 *((CHAR *)lpmii->dwTypeData) = 0;
4403 } else {
4404 int len;
4405 if (unicode)
4407 len = strlenW(menu->text);
4408 if(lpmii->dwTypeData && lpmii->cch)
4409 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4411 else
4413 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4414 0, NULL, NULL ) - 1;
4415 if(lpmii->dwTypeData && lpmii->cch)
4416 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4417 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4418 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4420 /* if we've copied a substring we return its length */
4421 if(lpmii->dwTypeData && lpmii->cch)
4422 if (lpmii->cch <= len + 1)
4423 lpmii->cch--;
4424 else
4425 lpmii->cch = len;
4426 else {
4427 /* return length of string */
4428 /* not on Win9x/ME if fType & MFT_BITMAP */
4429 lpmii->cch = len;
4434 if (lpmii->fMask & MIIM_FTYPE)
4435 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4437 if (lpmii->fMask & MIIM_BITMAP)
4438 lpmii->hbmpItem = menu->hbmpItem;
4440 if (lpmii->fMask & MIIM_STATE)
4441 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4443 if (lpmii->fMask & MIIM_ID)
4444 lpmii->wID = menu->wID;
4446 if (lpmii->fMask & MIIM_SUBMENU)
4447 lpmii->hSubMenu = menu->hSubMenu;
4448 else {
4449 /* hSubMenu is always cleared
4450 * (not on Win9x/ME ) */
4451 lpmii->hSubMenu = 0;
4454 if (lpmii->fMask & MIIM_CHECKMARKS) {
4455 lpmii->hbmpChecked = menu->hCheckBit;
4456 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4458 if (lpmii->fMask & MIIM_DATA)
4459 lpmii->dwItemData = menu->dwItemData;
4461 return TRUE;
4464 /**********************************************************************
4465 * GetMenuItemInfoA (USER32.@)
4467 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4468 LPMENUITEMINFOA lpmii)
4470 BOOL ret;
4471 MENUITEMINFOA mii;
4472 if( lpmii->cbSize != sizeof( mii) &&
4473 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4474 SetLastError( ERROR_INVALID_PARAMETER);
4475 return FALSE;
4477 memcpy( &mii, lpmii, lpmii->cbSize);
4478 mii.cbSize = sizeof( mii);
4479 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4480 (LPMENUITEMINFOW)&mii, FALSE);
4481 mii.cbSize = lpmii->cbSize;
4482 memcpy( lpmii, &mii, mii.cbSize);
4483 return ret;
4486 /**********************************************************************
4487 * GetMenuItemInfoW (USER32.@)
4489 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4490 LPMENUITEMINFOW lpmii)
4492 BOOL ret;
4493 MENUITEMINFOW mii;
4494 if( lpmii->cbSize != sizeof( mii) &&
4495 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4496 SetLastError( ERROR_INVALID_PARAMETER);
4497 return FALSE;
4499 memcpy( &mii, lpmii, lpmii->cbSize);
4500 mii.cbSize = sizeof( mii);
4501 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4502 mii.cbSize = lpmii->cbSize;
4503 memcpy( lpmii, &mii, mii.cbSize);
4504 return ret;
4508 /* set a menu item text from a ASCII or Unicode string */
4509 inline static void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4511 if (!text)
4512 menu->text = NULL;
4513 else if (unicode)
4515 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4516 strcpyW( menu->text, text );
4518 else
4520 LPCSTR str = (LPCSTR)text;
4521 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4522 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4523 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4528 /**********************************************************************
4529 * SetMenuItemInfo_common
4532 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4533 const MENUITEMINFOW *lpmii,
4534 BOOL unicode)
4536 if (!menu) return FALSE;
4538 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4540 if (lpmii->fMask & MIIM_TYPE ) {
4541 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4542 WARN("invalid combination of fMask bits used\n");
4543 /* this does not happen on Win9x/ME */
4544 SetLastError( ERROR_INVALID_PARAMETER);
4545 return FALSE;
4548 /* Remove the old type bits and replace them with the new ones */
4549 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4550 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4552 if (IS_STRING_ITEM(menu->fType)) {
4553 HeapFree(GetProcessHeap(), 0, menu->text);
4554 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4555 } else if( (menu->fType) & MFT_BITMAP)
4556 menu->hbmpItem = HBITMAP_32(LOWORD(lpmii->dwTypeData));
4559 if (lpmii->fMask & MIIM_FTYPE ) {
4560 if(( lpmii->fType & MFT_BITMAP)) {
4561 SetLastError( ERROR_INVALID_PARAMETER);
4562 return FALSE;
4564 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4565 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4567 if (lpmii->fMask & MIIM_STRING ) {
4568 /* free the string when used */
4569 HeapFree(GetProcessHeap(), 0, menu->text);
4570 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4573 if (lpmii->fMask & MIIM_STATE)
4575 /* Other menu items having MFS_DEFAULT are not converted
4576 to normal items */
4577 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4580 if (lpmii->fMask & MIIM_ID)
4581 menu->wID = lpmii->wID;
4583 if (lpmii->fMask & MIIM_SUBMENU) {
4584 menu->hSubMenu = lpmii->hSubMenu;
4585 if (menu->hSubMenu) {
4586 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4587 if (subMenu) {
4588 subMenu->wFlags |= MF_POPUP;
4589 menu->fType |= MF_POPUP;
4591 else {
4592 SetLastError( ERROR_INVALID_PARAMETER);
4593 return FALSE;
4596 else
4597 menu->fType &= ~MF_POPUP;
4600 if (lpmii->fMask & MIIM_CHECKMARKS)
4602 menu->hCheckBit = lpmii->hbmpChecked;
4603 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4605 if (lpmii->fMask & MIIM_DATA)
4606 menu->dwItemData = lpmii->dwItemData;
4608 if (lpmii->fMask & MIIM_BITMAP)
4609 menu->hbmpItem = lpmii->hbmpItem;
4611 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4612 menu->fType |= MFT_SEPARATOR;
4614 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4615 return TRUE;
4618 /**********************************************************************
4619 * SetMenuItemInfoA (USER32.@)
4621 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4622 const MENUITEMINFOA *lpmii)
4624 MENUITEMINFOA mii;
4625 if( lpmii->cbSize != sizeof( mii) &&
4626 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4627 SetLastError( ERROR_INVALID_PARAMETER);
4628 return FALSE;
4630 memcpy( &mii, lpmii, lpmii->cbSize);
4631 if( lpmii->cbSize != sizeof( mii)) {
4632 mii.cbSize = sizeof( mii);
4633 mii.hbmpItem = NULL;
4635 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4636 (const MENUITEMINFOW *)&mii, FALSE);
4639 /**********************************************************************
4640 * SetMenuItemInfoW (USER32.@)
4642 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4643 const MENUITEMINFOW *lpmii)
4645 MENUITEMINFOW mii;
4646 if( lpmii->cbSize != sizeof( mii) &&
4647 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4648 SetLastError( ERROR_INVALID_PARAMETER);
4649 return FALSE;
4651 memcpy( &mii, lpmii, lpmii->cbSize);
4652 if( lpmii->cbSize != sizeof( mii)) {
4653 mii.cbSize = sizeof( mii);
4654 mii.hbmpItem = NULL;
4656 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4657 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4660 /**********************************************************************
4661 * SetMenuDefaultItem (USER32.@)
4664 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4666 UINT i;
4667 POPUPMENU *menu;
4668 MENUITEM *item;
4670 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4672 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4674 /* reset all default-item flags */
4675 item = menu->items;
4676 for (i = 0; i < menu->nItems; i++, item++)
4678 item->fState &= ~MFS_DEFAULT;
4681 /* no default item */
4682 if ( -1 == uItem)
4684 return TRUE;
4687 item = menu->items;
4688 if ( bypos )
4690 if ( uItem >= menu->nItems ) return FALSE;
4691 item[uItem].fState |= MFS_DEFAULT;
4692 return TRUE;
4694 else
4696 for (i = 0; i < menu->nItems; i++, item++)
4698 if (item->wID == uItem)
4700 item->fState |= MFS_DEFAULT;
4701 return TRUE;
4706 return FALSE;
4709 /**********************************************************************
4710 * GetMenuDefaultItem (USER32.@)
4712 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4714 POPUPMENU *menu;
4715 MENUITEM * item;
4716 UINT i = 0;
4718 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4720 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4722 /* find default item */
4723 item = menu->items;
4725 /* empty menu */
4726 if (! item) return -1;
4728 while ( !( item->fState & MFS_DEFAULT ) )
4730 i++; item++;
4731 if (i >= menu->nItems ) return -1;
4734 /* default: don't return disabled items */
4735 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4737 /* search rekursiv when needed */
4738 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4740 UINT ret;
4741 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4742 if ( -1 != ret ) return ret;
4744 /* when item not found in submenu, return the popup item */
4746 return ( bypos ) ? i : item->wID;
4751 /**********************************************************************
4752 * InsertMenuItemA (USER32.@)
4754 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4755 const MENUITEMINFOA *lpmii)
4757 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4758 MENUITEMINFOA mii;
4759 if( lpmii->cbSize != sizeof( mii) &&
4760 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4761 SetLastError( ERROR_INVALID_PARAMETER);
4762 return FALSE;
4764 memcpy( &mii, lpmii, lpmii->cbSize);
4765 if( lpmii->cbSize != sizeof( mii)) {
4766 mii.cbSize = sizeof( mii);
4767 mii.hbmpItem = NULL;
4769 return SetMenuItemInfo_common(item, (const MENUITEMINFOW *)&mii, FALSE);
4773 /**********************************************************************
4774 * InsertMenuItemW (USER32.@)
4776 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4777 const MENUITEMINFOW *lpmii)
4779 MENUITEM *item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4780 MENUITEMINFOW mii;
4781 if( lpmii->cbSize != sizeof( mii) &&
4782 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4783 SetLastError( ERROR_INVALID_PARAMETER);
4784 return FALSE;
4786 memcpy( &mii, lpmii, lpmii->cbSize);
4787 if( lpmii->cbSize != sizeof( mii)) {
4788 mii.cbSize = sizeof( mii);
4789 mii.hbmpItem = NULL;
4791 return SetMenuItemInfo_common(item, &mii, TRUE);
4794 /**********************************************************************
4795 * CheckMenuRadioItem (USER32.@)
4798 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4799 UINT first, UINT last, UINT check,
4800 UINT bypos)
4802 MENUITEM *mifirst, *milast, *micheck;
4803 HMENU mfirst = hMenu, mlast = hMenu, mcheck = hMenu;
4805 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu, first, last, check, bypos);
4807 mifirst = MENU_FindItem (&mfirst, &first, bypos);
4808 milast = MENU_FindItem (&mlast, &last, bypos);
4809 micheck = MENU_FindItem (&mcheck, &check, bypos);
4811 if (mifirst == NULL || milast == NULL || micheck == NULL ||
4812 mifirst > milast || mfirst != mlast || mfirst != mcheck ||
4813 micheck > milast || micheck < mifirst)
4814 return FALSE;
4816 while (mifirst <= milast)
4818 if (mifirst == micheck)
4820 mifirst->fType |= MFT_RADIOCHECK;
4821 mifirst->fState |= MFS_CHECKED;
4822 } else {
4823 mifirst->fType &= ~MFT_RADIOCHECK;
4824 mifirst->fState &= ~MFS_CHECKED;
4826 mifirst++;
4829 return TRUE;
4833 /**********************************************************************
4834 * GetMenuItemRect (USER32.@)
4836 * ATTENTION: Here, the returned values in rect are the screen
4837 * coordinates of the item just like if the menu was
4838 * always on the upper left side of the application.
4841 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4842 LPRECT rect)
4844 POPUPMENU *itemMenu;
4845 MENUITEM *item;
4846 HWND referenceHwnd;
4848 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4850 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4851 referenceHwnd = hwnd;
4853 if(!hwnd)
4855 itemMenu = MENU_GetMenu(hMenu);
4856 if (itemMenu == NULL)
4857 return FALSE;
4859 if(itemMenu->hWnd == 0)
4860 return FALSE;
4861 referenceHwnd = itemMenu->hWnd;
4864 if ((rect == NULL) || (item == NULL))
4865 return FALSE;
4867 *rect = item->rect;
4869 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
4871 return TRUE;
4875 /**********************************************************************
4876 * SetMenuInfo (USER32.@)
4878 * FIXME
4879 * MIM_APPLYTOSUBMENUS
4880 * actually use the items to draw the menu
4882 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
4884 POPUPMENU *menu;
4886 TRACE("(%p %p)\n", hMenu, lpmi);
4888 if (lpmi && (lpmi->cbSize==sizeof(MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
4891 if (lpmi->fMask & MIM_BACKGROUND)
4892 menu->hbrBack = lpmi->hbrBack;
4894 if (lpmi->fMask & MIM_HELPID)
4895 menu->dwContextHelpID = lpmi->dwContextHelpID;
4897 if (lpmi->fMask & MIM_MAXHEIGHT)
4898 menu->cyMax = lpmi->cyMax;
4900 if (lpmi->fMask & MIM_MENUDATA)
4901 menu->dwMenuData = lpmi->dwMenuData;
4903 if (lpmi->fMask & MIM_STYLE)
4905 menu->dwStyle = lpmi->dwStyle;
4906 if (menu->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
4907 if (menu->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
4908 if (menu->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
4909 if (menu->dwStyle & MNS_NOTIFYBYPOS) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4912 return TRUE;
4914 return FALSE;
4917 /**********************************************************************
4918 * GetMenuInfo (USER32.@)
4920 * NOTES
4921 * win98/NT5.0
4924 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
4925 { POPUPMENU *menu;
4927 TRACE("(%p %p)\n", hMenu, lpmi);
4929 if (lpmi && (menu = MENU_GetMenu(hMenu)))
4932 if (lpmi->fMask & MIM_BACKGROUND)
4933 lpmi->hbrBack = menu->hbrBack;
4935 if (lpmi->fMask & MIM_HELPID)
4936 lpmi->dwContextHelpID = menu->dwContextHelpID;
4938 if (lpmi->fMask & MIM_MAXHEIGHT)
4939 lpmi->cyMax = menu->cyMax;
4941 if (lpmi->fMask & MIM_MENUDATA)
4942 lpmi->dwMenuData = menu->dwMenuData;
4944 if (lpmi->fMask & MIM_STYLE)
4945 lpmi->dwStyle = menu->dwStyle;
4947 return TRUE;
4949 return FALSE;
4953 /**********************************************************************
4954 * SetMenuContextHelpId (USER32.@)
4956 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
4958 LPPOPUPMENU menu;
4960 TRACE("(%p 0x%08lx)\n", hMenu, dwContextHelpID);
4962 if ((menu = MENU_GetMenu(hMenu)))
4964 menu->dwContextHelpID = dwContextHelpID;
4965 return TRUE;
4967 return FALSE;
4971 /**********************************************************************
4972 * GetMenuContextHelpId (USER32.@)
4974 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
4976 LPPOPUPMENU menu;
4978 TRACE("(%p)\n", hMenu);
4980 if ((menu = MENU_GetMenu(hMenu)))
4982 return menu->dwContextHelpID;
4984 return 0;
4987 /**********************************************************************
4988 * MenuItemFromPoint (USER32.@)
4990 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
4992 POPUPMENU *menu = MENU_GetMenu(hMenu);
4993 UINT pos;
4995 /*FIXME: Do we have to handle hWnd here? */
4996 if (!menu) return -1;
4997 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
4998 return pos;
5002 /**********************************************************************
5003 * translate_accelerator
5005 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5006 BYTE fVirt, WORD key, WORD cmd )
5008 INT mask = 0;
5009 UINT mesg = 0;
5011 if (wParam != key) return FALSE;
5013 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5014 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5015 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5017 if (message == WM_CHAR || message == WM_SYSCHAR)
5019 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5021 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", wParam & 0xff);
5022 goto found;
5025 else
5027 if(fVirt & FVIRTKEY)
5029 TRACE_(accel)("found accel for virt_key %04x (scan %04x)\n",
5030 wParam, 0xff & HIWORD(lParam));
5032 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5033 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5035 else
5037 if (!(lParam & 0x01000000)) /* no special_key */
5039 if ((fVirt & FALT) && (lParam & 0x20000000))
5040 { /* ^^ ALT pressed */
5041 TRACE_(accel)("found accel for Alt-%c\n", wParam & 0xff);
5042 goto found;
5047 return FALSE;
5049 found:
5050 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5051 mesg = 1;
5052 else
5054 HMENU hMenu, hSubMenu, hSysMenu;
5055 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5057 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5058 hSysMenu = get_win_sys_menu( hWnd );
5060 /* find menu item and ask application to initialize it */
5061 /* 1. in the system menu */
5062 hSubMenu = hSysMenu;
5063 nPos = cmd;
5064 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5066 if (GetCapture())
5067 mesg = 2;
5068 if (!IsWindowEnabled(hWnd))
5069 mesg = 3;
5070 else
5072 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5073 if(hSubMenu != hSysMenu)
5075 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5076 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5077 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5079 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5082 else /* 2. in the window's menu */
5084 hSubMenu = hMenu;
5085 nPos = cmd;
5086 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5088 if (GetCapture())
5089 mesg = 2;
5090 if (!IsWindowEnabled(hWnd))
5091 mesg = 3;
5092 else
5094 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5095 if(hSubMenu != hMenu)
5097 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5098 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5099 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5101 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5106 if (mesg == 0)
5108 if (uSysStat != (UINT)-1)
5110 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5111 mesg=4;
5112 else
5113 mesg=WM_SYSCOMMAND;
5115 else
5117 if (uStat != (UINT)-1)
5119 if (IsIconic(hWnd))
5120 mesg=5;
5121 else
5123 if (uStat & (MF_DISABLED|MF_GRAYED))
5124 mesg=6;
5125 else
5126 mesg=WM_COMMAND;
5129 else
5130 mesg=WM_COMMAND;
5135 if( mesg==WM_COMMAND )
5137 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5138 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5140 else if( mesg==WM_SYSCOMMAND )
5142 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5143 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5145 else
5147 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5148 * #0: unknown (please report!)
5149 * #1: for WM_KEYUP,WM_SYSKEYUP
5150 * #2: mouse is captured
5151 * #3: window is disabled
5152 * #4: it's a disabled system menu option
5153 * #5: it's a menu option, but window is iconic
5154 * #6: it's a menu option, but disabled
5156 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5157 if(mesg==0)
5158 ERR_(accel)(" unknown reason - please report!\n");
5160 return TRUE;
5163 /**********************************************************************
5164 * TranslateAcceleratorA (USER32.@)
5165 * TranslateAccelerator (USER32.@)
5167 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5169 /* YES, Accel16! */
5170 LPACCEL16 lpAccelTbl;
5171 int i;
5172 WPARAM wParam;
5174 if (!hWnd || !msg) return 0;
5176 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5178 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5179 return 0;
5182 wParam = msg->wParam;
5184 switch (msg->message)
5186 case WM_KEYDOWN:
5187 case WM_SYSKEYDOWN:
5188 break;
5190 case WM_CHAR:
5191 case WM_SYSCHAR:
5193 char ch = LOWORD(wParam);
5194 WCHAR wch;
5195 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5196 wParam = MAKEWPARAM(wch, HIWORD(wParam));
5198 break;
5200 default:
5201 return 0;
5204 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5205 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5206 i = 0;
5209 if (translate_accelerator( hWnd, msg->message, wParam, msg->lParam,
5210 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5211 return 1;
5212 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5214 return 0;
5217 /**********************************************************************
5218 * TranslateAcceleratorW (USER32.@)
5220 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5222 /* YES, Accel16! */
5223 LPACCEL16 lpAccelTbl;
5224 int i;
5226 if (!hWnd || !msg) return 0;
5228 if (!hAccel || !(lpAccelTbl = (LPACCEL16) LockResource16(HACCEL_16(hAccel))))
5230 WARN_(accel)("invalid accel handle=%p\n", hAccel);
5231 return 0;
5234 switch (msg->message)
5236 case WM_KEYDOWN:
5237 case WM_SYSKEYDOWN:
5238 case WM_CHAR:
5239 case WM_SYSCHAR:
5240 break;
5242 default:
5243 return 0;
5246 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5247 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5248 i = 0;
5251 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5252 lpAccelTbl[i].fVirt, lpAccelTbl[i].key, lpAccelTbl[i].cmd))
5253 return 1;
5254 } while ((lpAccelTbl[i++].fVirt & 0x80) == 0);
5256 return 0;