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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
44 #include "wine/port.h"
53 #include "wine/winbase16.h"
54 #include "wine/winuser16.h"
56 #include "wine/server.h"
57 #include "wine/unicode.h"
60 #include "user_private.h"
61 #include "wine/debug.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
64 WINE_DECLARE_DEBUG_CHANNEL(accel
);
66 /* internal popup menu window messages */
68 #define MM_SETMENUHANDLE (WM_USER + 0)
69 #define MM_GETMENUHANDLE (WM_USER + 1)
71 /* Menu item structure */
73 /* ----------- MENUITEMINFO Stuff ----------- */
74 UINT fType
; /* Item type. */
75 UINT fState
; /* Item state. */
76 UINT_PTR wID
; /* Item id. */
77 HMENU hSubMenu
; /* Pop-up menu. */
78 HBITMAP hCheckBit
; /* Bitmap when checked. */
79 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
80 LPWSTR text
; /* Item text. */
81 ULONG_PTR dwItemData
; /* Application defined. */
82 LPWSTR dwTypeData
; /* depends on fMask */
83 HBITMAP hbmpItem
; /* bitmap */
84 /* ----------- Wine stuff ----------- */
85 RECT rect
; /* Item area (relative to menu window) */
86 UINT xTab
; /* X position of text after Tab */
87 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
91 /* Popup menu structure */
93 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD wMagic
; /* Magic number */
95 WORD Width
; /* Width of the whole menu */
96 WORD Height
; /* Height of the whole menu */
97 UINT nItems
; /* Number of items in the menu */
98 HWND hWnd
; /* Window containing the menu */
99 MENUITEM
*items
; /* Array of menu items */
100 UINT FocusedItem
; /* Currently focused item */
101 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
102 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
103 BOOL bScrolling
; /* Scroll arrows are active */
104 UINT nScrollPos
; /* Current scroll position */
105 UINT nTotalHeight
; /* Total height of menu items inside menu */
106 /* ------------ MENUINFO members ------ */
107 DWORD dwStyle
; /* Extended menu style */
108 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
109 HBRUSH hbrBack
; /* brush for menu background */
110 DWORD dwContextHelpID
;
111 DWORD dwMenuData
; /* application defined value */
112 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
113 SIZE maxBmpSize
; /* Maximum size of the bitmap items */
114 } POPUPMENU
, *LPPOPUPMENU
;
116 /* internal flags for menu tracking */
118 #define TF_ENDMENU 0x0001
119 #define TF_SUSPENDPOPUP 0x0002
120 #define TF_SKIPREMOVE 0x0004
125 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
126 HMENU hTopMenu
; /* initial menu */
127 HWND hOwnerWnd
; /* where notifications are sent */
131 #define MENU_MAGIC 0x554d /* 'MU' */
136 /* Internal MENU_TrackMenu() flags */
137 #define TPM_INTERNAL 0xF0000000
138 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
139 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
140 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
142 /* Space between 2 columns */
143 #define MENU_COL_SPACE 4
145 /* top and bottom margins for popup menus */
146 #define MENU_TOP_MARGIN 3
147 #define MENU_BOTTOM_MARGIN 2
149 /* (other menu->FocusedItem values give the position of the focused item) */
150 #define NO_SELECTED_ITEM 0xffff
152 #define MENU_ITEM_TYPE(flags) \
153 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
155 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
156 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
157 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
159 #define IS_SYSTEM_MENU(menu) \
160 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
162 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
163 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
164 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
165 MF_POPUP | MF_SYSMENU | MF_HELP)
166 #define STATE_MASK (~TYPE_MASK)
168 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
170 static SIZE menucharsize
;
171 static UINT ODitemheight
; /* default owner drawn item height */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup
;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu
= FALSE
;
180 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
182 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class
=
189 POPUPMENU_CLASS_ATOMA
, /* name */
190 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
191 NULL
, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc
, /* procW */
193 sizeof(HMENU
), /* extra */
194 IDC_ARROW
, /* cursor */
195 (HBRUSH
)(COLOR_MENU
+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
208 #define MENUOUT(text) \
209 TRACE("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
216 static void do_debug_print_menuitem(const char *prefix
, MENUITEM
* mp
,
219 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
220 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "HBMMENU_MBAR_CLOSE",
221 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
222 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
223 TRACE("%s ", prefix
);
225 UINT flags
= mp
->fType
;
226 TRACE( "{ ID=0x%x", mp
->wID
);
228 TRACE( ", Sub=%p", mp
->hSubMenu
);
232 MENUFLAG( MFT_SEPARATOR
, "sep");
233 MENUFLAG( MFT_OWNERDRAW
, "own");
234 MENUFLAG( MFT_BITMAP
, "bit");
235 MENUFLAG(MF_POPUP
, "pop");
236 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
237 MENUFLAG(MFT_MENUBREAK
, "brk");
238 MENUFLAG(MFT_RADIOCHECK
, "radio");
239 MENUFLAG(MFT_RIGHTORDER
, "rorder");
240 MENUFLAG(MF_SYSMENU
, "sys");
241 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
243 TRACE( "+0x%x", flags
);
249 MENUFLAG(MFS_GRAYED
, "grey");
250 MENUFLAG(MFS_DEFAULT
, "default");
251 MENUFLAG(MFS_DISABLED
, "dis");
252 MENUFLAG(MFS_CHECKED
, "check");
253 MENUFLAG(MFS_HILITE
, "hi");
254 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
255 MENUFLAG(MF_MOUSESELECT
, "mouse");
257 TRACE( "+0x%x", flags
);
260 TRACE( ", Chk=%p", mp
->hCheckBit
);
262 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
264 TRACE( ", Text=%s", debugstr_w(mp
->text
));
266 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
269 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
270 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
272 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
277 TRACE(" %s\n", postfix
);
284 /***********************************************************************
287 * Validate the given menu handle and returns the menu structure pointer.
289 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
291 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
292 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
294 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
300 /***********************************************************************
303 * Get the system menu of a window
305 static HMENU
get_win_sys_menu( HWND hwnd
)
308 WND
*win
= WIN_GetPtr( hwnd
);
309 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
312 WIN_ReleasePtr( win
);
317 /***********************************************************************
320 static HFONT
get_menu_font( BOOL bold
)
322 static HFONT hMenuFont
, hMenuFontBold
;
324 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
328 NONCLIENTMETRICSW ncm
;
331 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
332 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
336 ncm
.lfMenuFont
.lfWeight
+= 300;
337 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
339 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
340 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
344 /* another thread beat us to it */
352 /***********************************************************************
355 static HBITMAP
get_arrow_bitmap(void)
357 static HBITMAP arrow_bitmap
;
359 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
363 /***********************************************************************
364 * get_down_arrow_bitmap
366 static HBITMAP
get_down_arrow_bitmap(void)
368 static HBITMAP arrow_bitmap
;
370 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
374 /***********************************************************************
375 * get_down_arrow_inactive_bitmap
377 static HBITMAP
get_down_arrow_inactive_bitmap(void)
379 static HBITMAP arrow_bitmap
;
381 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
385 /***********************************************************************
386 * get_up_arrow_bitmap
388 static HBITMAP
get_up_arrow_bitmap(void)
390 static HBITMAP arrow_bitmap
;
392 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
396 /***********************************************************************
397 * get_up_arrow_inactive_bitmap
399 static HBITMAP
get_up_arrow_inactive_bitmap(void)
401 static HBITMAP arrow_bitmap
;
403 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
407 /***********************************************************************
410 * Return the default system menu.
412 static HMENU
MENU_CopySysPopup(void)
414 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
415 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
418 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
419 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
420 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
423 ERR("Unable to load default system menu\n" );
425 TRACE("returning %p.\n", hMenu
);
431 /**********************************************************************
434 * Create a copy of the system menu. System menu in Windows is
435 * a special menu bar with the single entry - system menu popup.
436 * This popup is presented to the outside world as a "system menu".
437 * However, the real system menu handle is sometimes seen in the
438 * WM_MENUSELECT parameters (and Word 6 likes it this way).
440 HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
444 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
445 if ((hMenu
= CreateMenu()))
447 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
448 menu
->wFlags
= MF_SYSMENU
;
449 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
450 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
453 hPopupMenu
= MENU_CopySysPopup();
457 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
458 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
460 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
461 (UINT_PTR
)hPopupMenu
, NULL
);
463 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
464 menu
->items
[0].fState
= 0;
465 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
467 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
470 DestroyMenu( hMenu
);
472 ERR("failed to load system menu!\n");
477 /***********************************************************************
478 * MENU_InitSysMenuPopup
480 * Grey the appropriate items in System menu.
482 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
486 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
487 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
488 gray
= ((style
& WS_MAXIMIZE
) != 0);
489 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
490 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
491 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
492 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
493 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
494 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
495 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
496 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
498 /* The menu item must keep its state if it's disabled */
500 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
504 /******************************************************************************
506 * UINT MENU_GetStartOfNextColumn(
509 *****************************************************************************/
511 static UINT
MENU_GetStartOfNextColumn(
514 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
518 return NO_SELECTED_ITEM
;
520 i
= menu
->FocusedItem
+ 1;
521 if( i
== NO_SELECTED_ITEM
)
524 for( ; i
< menu
->nItems
; ++i
) {
525 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
529 return NO_SELECTED_ITEM
;
533 /******************************************************************************
535 * UINT MENU_GetStartOfPrevColumn(
538 *****************************************************************************/
540 static UINT
MENU_GetStartOfPrevColumn(
543 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
547 return NO_SELECTED_ITEM
;
549 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
550 return NO_SELECTED_ITEM
;
552 /* Find the start of the column */
554 for(i
= menu
->FocusedItem
; i
!= 0 &&
555 !(menu
->items
[i
].fType
& MF_MENUBARBREAK
);
559 return NO_SELECTED_ITEM
;
561 for(--i
; i
!= 0; --i
) {
562 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
566 TRACE("ret %d.\n", i
);
573 /***********************************************************************
576 * Find a menu item. Return a pointer on the item, and modifies *hmenu
577 * in case the item was in a sub-menu.
579 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
582 MENUITEM
*fallback
= NULL
;
585 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
586 if (wFlags
& MF_BYPOSITION
)
588 if (*nPos
>= menu
->nItems
) return NULL
;
589 return &menu
->items
[*nPos
];
593 MENUITEM
*item
= menu
->items
;
594 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
596 if (item
->fType
& MF_POPUP
)
598 HMENU hsubmenu
= item
->hSubMenu
;
599 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
605 if ((UINT_PTR
)item
->hSubMenu
== *nPos
)
606 fallback
= item
; /* fallback to this item if nothing else found */
608 else if (item
->wID
== *nPos
)
618 /***********************************************************************
621 * Find a Sub menu. Return the position of the submenu, and modifies
622 * *hmenu in case it is found in another sub-menu.
623 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
625 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
630 if (((*hmenu
)==(HMENU
)0xffff) ||
631 (!(menu
= MENU_GetMenu(*hmenu
))))
632 return NO_SELECTED_ITEM
;
634 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
635 if(!(item
->fType
& MF_POPUP
)) continue;
636 if (item
->hSubMenu
== hSubTarget
) {
640 HMENU hsubmenu
= item
->hSubMenu
;
641 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
642 if (pos
!= NO_SELECTED_ITEM
) {
648 return NO_SELECTED_ITEM
;
651 /***********************************************************************
654 static void MENU_FreeItemData( MENUITEM
* item
)
657 HeapFree( GetProcessHeap(), 0, item
->text
);
660 /***********************************************************************
661 * MENU_AdjustMenuItemRect
663 * Adjust menu item rectangle according to scrolling state.
666 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
668 if (menu
->bScrolling
)
670 UINT arrow_bitmap_width
, arrow_bitmap_height
;
673 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
674 arrow_bitmap_width
= bmp
.bmWidth
;
675 arrow_bitmap_height
= bmp
.bmHeight
;
676 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
677 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
682 /***********************************************************************
683 * MENU_FindItemByCoords
685 * Find the item at the specified coordinates (screen coords). Does
686 * not work for child windows and therefore should not be called for
687 * an arbitrary system menu.
689 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
690 POINT pt
, UINT
*pos
)
697 if (!GetWindowRect(menu
->hWnd
,&wrect
)) return NULL
;
698 pt
.x
-= wrect
.left
;pt
.y
-= wrect
.top
;
700 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
703 MENU_AdjustMenuItemRect(menu
, &rect
);
704 if ((pt
.x
>= rect
.left
) && (pt
.x
< rect
.right
) &&
705 (pt
.y
>= rect
.top
) && (pt
.y
< rect
.bottom
))
715 /***********************************************************************
718 * Find the menu item selected by a key press.
719 * Return item id, -1 if none, -2 if we should close the menu.
721 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
722 WCHAR key
, BOOL forceMenuChar
)
724 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
726 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
730 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
731 MENUITEM
*item
= menu
->items
;
738 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
742 WCHAR
*p
= item
->text
- 2;
745 p
= strchrW (p
+ 2, '&');
747 while (p
!= NULL
&& p
[1] == '&');
748 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
752 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
753 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
754 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
755 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
761 /***********************************************************************
762 * MENU_GetBitmapItemSize
764 * Get the size of a bitmap item.
766 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
770 HBITMAP bmp
= lpitem
->hbmpItem
;
772 size
->cx
= size
->cy
= 0;
774 /* check if there is a magic menu item associated with this item */
775 switch( (INT_PTR
) bmp
)
777 case (INT_PTR
)HBMMENU_CALLBACK
:
779 MEASUREITEMSTRUCT measItem
;
780 measItem
.CtlType
= ODT_MENU
;
782 measItem
.itemID
= lpitem
->wID
;
783 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
784 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
785 measItem
.itemData
= lpitem
->dwItemData
;
786 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
787 size
->cx
= measItem
.itemWidth
;
788 size
->cy
= measItem
.itemHeight
;
792 case (INT_PTR
)HBMMENU_SYSTEM
:
793 if (lpitem
->dwItemData
)
795 bmp
= (HBITMAP
)lpitem
->dwItemData
;
799 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
800 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
801 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
802 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
803 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
804 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
807 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
808 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
809 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
810 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
811 FIXME("Magic %p not implemented\n", bmp
);
814 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
816 size
->cx
= bm
.bmWidth
;
817 size
->cy
= bm
.bmHeight
;
821 /***********************************************************************
822 * MENU_DrawBitmapItem
824 * Draw a bitmap item.
826 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
827 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
833 int w
= rect
->right
- rect
->left
;
834 int h
= rect
->bottom
- rect
->top
;
837 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
840 /* Check if there is a magic menu item associated with this item */
841 if (IS_MAGIC_BITMAP(hbmToDraw
))
846 switch((INT_PTR
)hbmToDraw
)
848 case (INT_PTR
)HBMMENU_SYSTEM
:
849 if (lpitem
->dwItemData
)
851 bmp
= (HBITMAP
)lpitem
->dwItemData
;
852 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
856 static HBITMAP hBmpSysMenu
;
858 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
860 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
861 /* only use right half of the bitmap */
862 bmp_xoffset
= bm
.bmWidth
/ 2;
863 bm
.bmWidth
-= bmp_xoffset
;
866 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
867 flags
= DFCS_CAPTIONRESTORE
;
869 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
870 flags
= DFCS_CAPTIONMIN
;
872 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
873 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
875 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
876 flags
= DFCS_CAPTIONCLOSE
;
878 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
879 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
881 case (INT_PTR
)HBMMENU_CALLBACK
:
883 DRAWITEMSTRUCT drawItem
;
884 drawItem
.CtlType
= ODT_MENU
;
886 drawItem
.itemID
= lpitem
->wID
;
887 drawItem
.itemAction
= odaction
;
888 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
889 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
890 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
891 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
892 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
893 drawItem
.hwndItem
= (HWND
)hmenu
;
895 drawItem
.itemData
= lpitem
->dwItemData
;
896 drawItem
.rcItem
= *rect
;
897 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
901 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
902 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
903 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
904 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
906 FIXME("Magic %p not implemented\n", hbmToDraw
);
910 InflateRect( &r
, -1, -1 );
911 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
912 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
916 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
919 hdcMem
= CreateCompatibleDC( hdc
);
920 SelectObject( hdcMem
, bmp
);
922 /* handle fontsize > bitmap_height */
923 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
925 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
926 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
927 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
928 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
933 /***********************************************************************
936 * Calculate the size of the menu item and store it in lpitem->rect.
938 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
939 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
942 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
943 UINT arrow_bitmap_width
;
947 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
948 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
949 (menuBar
? " (MenuBar)" : ""));
951 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
952 arrow_bitmap_width
= bm
.bmWidth
;
954 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
955 if( !menucharsize
.cx
) {
956 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
957 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
958 * but it is unlikely an application will depend on that */
959 ODitemheight
= HIWORD( GetDialogBaseUnits());
962 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
964 if (lpitem
->fType
& MF_OWNERDRAW
)
966 MEASUREITEMSTRUCT mis
;
967 mis
.CtlType
= ODT_MENU
;
969 mis
.itemID
= lpitem
->wID
;
970 mis
.itemData
= lpitem
->dwItemData
;
971 mis
.itemHeight
= ODitemheight
;
973 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
974 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
975 * width of a menufont character to the width of an owner-drawn menu.
977 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
979 /* under at least win95 you seem to be given a standard
980 height for the menu and the height value is ignored */
981 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
983 lpitem
->rect
.bottom
+= mis
.itemHeight
;
985 TRACE("id=%04x size=%ldx%ld\n",
986 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
987 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
991 if (lpitem
->fType
& MF_SEPARATOR
)
993 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
995 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1003 if (lpitem
->hbmpItem
) {
1006 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1007 /* Keep the size of the bitmap in callback mode to be able
1008 * to draw it correctly */
1009 lpitem
->bmpsize
= size
;
1010 lppop
->maxBmpSize
.cx
= max( lppop
->maxBmpSize
.cx
, size
.cx
);
1011 lppop
->maxBmpSize
.cy
= max( lppop
->maxBmpSize
.cy
, size
.cy
);
1012 lpitem
->rect
.right
+= size
.cx
+ 2;
1013 itemheight
= size
.cy
+ 2;
1015 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1016 lpitem
->rect
.right
+= check_bitmap_width
;
1017 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1018 lpitem
->xTab
= lpitem
->rect
.right
;
1019 lpitem
->rect
.right
+= arrow_bitmap_width
;
1020 } else if (lpitem
->hbmpItem
) { /* menuBar */
1023 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1024 lpitem
->bmpsize
= size
;
1025 lpitem
->rect
.right
+= size
.cx
;
1026 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1027 itemheight
= size
.cy
;
1030 /* it must be a text item - unless it's the system menu */
1031 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1032 HFONT hfontOld
= NULL
;
1033 RECT rc
= lpitem
->rect
;
1034 LONG txtheight
, txtwidth
;
1036 if ( lpitem
->fState
& MFS_DEFAULT
) {
1037 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1040 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1041 DT_SINGLELINE
|DT_CALCRECT
);
1042 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1043 itemheight
= max( max( itemheight
, txtheight
),
1044 GetSystemMetrics( SM_CYMENU
) - 1);
1045 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1047 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1050 int n
= (int)( p
- lpitem
->text
);
1051 /* Item contains a tab (only meaningful in popup menus) */
1052 /* get text size before the tab */
1053 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1054 DT_SINGLELINE
|DT_CALCRECT
);
1055 txtwidth
= rc
.right
- rc
.left
;
1056 p
+= 1; /* advance past the Tab */
1057 /* get text size after the tab */
1058 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1059 DT_SINGLELINE
|DT_CALCRECT
);
1060 lpitem
->xTab
+= txtwidth
;
1061 txtheight
= max( txtheight
, tmpheight
);
1062 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1063 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1065 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1066 DT_SINGLELINE
|DT_CALCRECT
);
1067 txtwidth
= rc
.right
- rc
.left
;
1068 lpitem
->xTab
+= txtwidth
;
1070 lpitem
->rect
.right
+= 2 + txtwidth
;
1071 itemheight
= max( itemheight
,
1072 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1074 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1075 } else if( menuBar
) {
1076 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1078 lpitem
->rect
.bottom
+= itemheight
;
1079 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1083 /***********************************************************************
1084 * MENU_GetMaxPopupHeight
1087 MENU_GetMaxPopupHeight(LPPOPUPMENU lppop
)
1090 return lppop
->cyMax
;
1091 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1095 /***********************************************************************
1096 * MENU_PopupMenuCalcSize
1098 * Calculate the size of a popup menu.
1100 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, HWND hwndOwner
)
1105 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1107 lppop
->Width
= lppop
->Height
= 0;
1108 if (lppop
->nItems
== 0) return;
1111 SelectObject( hdc
, get_menu_font(FALSE
));
1116 lppop
->maxBmpSize
.cx
= 0;
1117 lppop
->maxBmpSize
.cy
= 0;
1119 while (start
< lppop
->nItems
)
1121 lpitem
= &lppop
->items
[start
];
1123 if( lpitem
->fType
& MF_MENUBREAK
)
1124 orgX
+= MENU_COL_SPACE
;
1125 orgY
= MENU_TOP_MARGIN
;
1127 maxTab
= maxTabWidth
= 0;
1128 /* Parse items until column break or end of menu */
1129 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1132 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1134 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1136 if (lpitem
->fType
& MF_MENUBARBREAK
) orgX
++;
1137 maxX
= max( maxX
, lpitem
->rect
.right
);
1138 orgY
= lpitem
->rect
.bottom
;
1139 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1141 maxTab
= max( maxTab
, lpitem
->xTab
);
1142 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1146 /* Finish the column (set all items to the largest width found) */
1147 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1148 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1150 lpitem
->rect
.right
= maxX
;
1151 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1152 lpitem
->xTab
= maxTab
;
1155 lppop
->Height
= max( lppop
->Height
, orgY
);
1158 lppop
->Width
= maxX
;
1160 /* space for 3d border */
1161 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1164 /* Adjust popup height if it exceeds maximum */
1165 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1166 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1167 if (lppop
->Height
>= maxHeight
)
1169 lppop
->Height
= maxHeight
;
1170 lppop
->bScrolling
= TRUE
;
1174 lppop
->bScrolling
= FALSE
;
1177 ReleaseDC( 0, hdc
);
1181 /***********************************************************************
1182 * MENU_MenuBarCalcSize
1184 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1185 * height is off by 1 pixel which causes lengthy window relocations when
1186 * active document window is maximized/restored.
1188 * Calculate the size of the menu bar.
1190 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1191 LPPOPUPMENU lppop
, HWND hwndOwner
)
1194 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1196 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1197 if (lppop
->nItems
== 0) return;
1198 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1199 lppop
->Width
= lprect
->right
- lprect
->left
;
1201 maxY
= lprect
->top
+1;
1204 lppop
->maxBmpSize
.cx
= 0;
1205 lppop
->maxBmpSize
.cy
= 0;
1206 while (start
< lppop
->nItems
)
1208 lpitem
= &lppop
->items
[start
];
1209 orgX
= lprect
->left
;
1212 /* Parse items until line break or end of menu */
1213 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1215 if ((helpPos
== -1) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1217 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1219 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1220 debug_print_menuitem (" item: ", lpitem
, "");
1221 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1223 if (lpitem
->rect
.right
> lprect
->right
)
1225 if (i
!= start
) break;
1226 else lpitem
->rect
.right
= lprect
->right
;
1228 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1229 orgX
= lpitem
->rect
.right
;
1232 /* Finish the line (set all items to the largest height found) */
1233 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1236 lprect
->bottom
= maxY
;
1237 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1239 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1240 /* the last item (if several lines, only move the last line) */
1241 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1242 orgY
= lpitem
->rect
.top
;
1243 orgX
= lprect
->right
;
1244 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1245 if ( (helpPos
==-1) || (helpPos
>i
) )
1247 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1248 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1249 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1250 lpitem
->rect
.right
= orgX
;
1251 orgX
= lpitem
->rect
.left
;
1256 /***********************************************************************
1257 * MENU_DrawScrollArrows
1259 * Draw scroll arrows.
1262 MENU_DrawScrollArrows(LPPOPUPMENU lppop
, HDC hdc
)
1264 HDC hdcMem
= CreateCompatibleDC(hdc
);
1265 HBITMAP hOrigBitmap
;
1266 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1270 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1271 arrow_bitmap_width
= bmp
.bmWidth
;
1272 arrow_bitmap_height
= bmp
.bmHeight
;
1275 if (lppop
->nScrollPos
)
1276 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1278 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1281 rect
.right
= lppop
->Width
;
1282 rect
.bottom
= arrow_bitmap_height
;
1283 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1284 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1285 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1286 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1287 rect
.bottom
= lppop
->Height
;
1288 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1289 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1290 SelectObject(hdcMem
, get_down_arrow_bitmap());
1292 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1293 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1294 lppop
->Height
- arrow_bitmap_height
,
1295 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1296 SelectObject(hdcMem
, hOrigBitmap
);
1301 /***********************************************************************
1304 * Draws the popup-menu arrow.
1306 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1307 UINT arrow_bitmap_height
)
1309 HDC hdcMem
= CreateCompatibleDC( hdc
);
1310 HBITMAP hOrigBitmap
;
1312 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1313 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1314 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1315 arrow_bitmap_width
, arrow_bitmap_height
,
1316 hdcMem
, 0, 0, SRCCOPY
);
1317 SelectObject( hdcMem
, hOrigBitmap
);
1320 /***********************************************************************
1323 * Draw a single menu item.
1325 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1326 UINT height
, BOOL menuBar
, UINT odaction
)
1329 BOOL flat_menu
= FALSE
;
1331 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1332 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1335 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1339 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1340 arrow_bitmap_width
= bmp
.bmWidth
;
1341 arrow_bitmap_height
= bmp
.bmHeight
;
1344 if (lpitem
->fType
& MF_SYSMENU
)
1346 if( !IsIconic(hwnd
) )
1347 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1351 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1352 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1356 if (lpitem
->fState
& MF_HILITE
)
1358 if(menuBar
&& !flat_menu
) {
1359 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1360 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1362 if(lpitem
->fState
& MF_GRAYED
)
1363 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1365 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1366 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1371 if (lpitem
->fState
& MF_GRAYED
)
1372 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1374 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1375 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1378 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1379 rect
= lpitem
->rect
;
1380 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1382 if (lpitem
->fType
& MF_OWNERDRAW
)
1385 ** Experimentation under Windows reveals that an owner-drawn
1386 ** menu is given the rectangle which includes the space it requested
1387 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1388 ** and a popup-menu arrow. This is the value of lpitem->rect.
1389 ** Windows will leave all drawing to the application except for
1390 ** the popup-menu arrow. Windows always draws that itself, after
1391 ** the menu owner has finished drawing.
1395 dis
.CtlType
= ODT_MENU
;
1397 dis
.itemID
= lpitem
->wID
;
1398 dis
.itemData
= lpitem
->dwItemData
;
1400 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1401 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1402 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1403 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1404 dis
.hwndItem
= (HWND
)hmenu
;
1407 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1408 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1409 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1410 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1411 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1412 /* Draw the popup-menu arrow */
1413 if (lpitem
->fType
& MF_POPUP
)
1414 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1415 arrow_bitmap_height
);
1419 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1421 if (lpitem
->fState
& MF_HILITE
)
1425 InflateRect (&rect
, -1, -1);
1426 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1427 InflateRect (&rect
, 1, 1);
1428 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1433 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1435 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1439 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1441 SetBkMode( hdc
, TRANSPARENT
);
1443 /* vertical separator */
1444 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1450 rc
.bottom
= height
- 3;
1453 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1454 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1455 LineTo( hdc
, rc
.left
, rc
.bottom
);
1456 SelectObject( hdc
, oldPen
);
1459 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1462 /* horizontal separator */
1463 if (lpitem
->fType
& MF_SEPARATOR
)
1470 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1473 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1474 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1475 LineTo( hdc
, rc
.right
, rc
.top
);
1476 SelectObject( hdc
, oldPen
);
1479 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1483 /* helper lines for debugging */
1484 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1485 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1486 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1487 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1490 if (lpitem
->hbmpItem
) {
1491 /* calculate the bitmap rectangle in coordinates relative
1492 * to the item rectangle */
1494 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1497 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1500 if( !(menu
->dwStyle
& ( MNS_CHECKORBMP
| MNS_NOCHECK
)))
1501 bmprc
.left
+= GetSystemMetrics( SM_CXMENUCHECK
);
1503 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1504 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1507 bmprc
.top
= (lpitem
->rect
.bottom
- lpitem
->rect
.top
-
1508 lpitem
->bmpsize
.cy
) / 2;
1509 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1515 INT y
= rect
.top
+ rect
.bottom
;
1517 int checked
= FALSE
;
1518 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1519 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1520 /* Draw the check mark
1523 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1525 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1526 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1527 lpitem
->hUnCheckBit
;
1528 if (bm
) /* we have a custom bitmap */
1530 HDC hdcMem
= CreateCompatibleDC( hdc
);
1532 SelectObject( hdcMem
, bm
);
1533 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1534 check_bitmap_width
, check_bitmap_height
,
1535 hdcMem
, 0, 0, SRCCOPY
);
1539 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1542 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1543 check_bitmap_height
, 1, 1, NULL
);
1544 HDC hdcMem
= CreateCompatibleDC( hdc
);
1546 SelectObject( hdcMem
, bm
);
1547 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1548 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1549 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1550 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1551 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1552 hdcMem
, 0, 0, SRCCOPY
);
1558 if( lpitem
->hbmpItem
&&
1559 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1561 /* some applications make this assumption on the DC's origin */
1562 SetViewportOrgEx( hdc
, lpitem
->rect
.left
, lpitem
->rect
.top
, &origorg
);
1563 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1565 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1567 /* Draw the popup-menu arrow */
1568 if (lpitem
->fType
& MF_POPUP
)
1569 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1570 arrow_bitmap_height
);
1572 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1573 rect
.left
+= check_bitmap_width
;
1574 rect
.right
-= arrow_bitmap_width
;
1576 else if( lpitem
->hbmpItem
)
1577 { /* Draw the bitmap */
1580 SetViewportOrgEx( hdc
, lpitem
->rect
.left
, lpitem
->rect
.top
, &origorg
);
1581 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1583 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1585 /* process text if present */
1591 UINT uFormat
= (menuBar
) ?
1592 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1593 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1595 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1596 rect
.left
+= menu
->maxBmpSize
.cx
;
1598 if ( lpitem
->fState
& MFS_DEFAULT
)
1600 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1604 if( lpitem
->hbmpItem
)
1605 rect
.left
+= lpitem
->bmpsize
.cx
;
1606 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1607 rect
.left
+= menucharsize
.cx
;
1608 rect
.right
-= menucharsize
.cx
;
1611 for (i
= 0; lpitem
->text
[i
]; i
++)
1612 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1615 if(lpitem
->fState
& MF_GRAYED
)
1617 if (!(lpitem
->fState
& MF_HILITE
) )
1619 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1620 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1621 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1622 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1624 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1627 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1629 /* paint the shortcut text */
1630 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1632 if (lpitem
->text
[i
] == '\t')
1634 rect
.left
= lpitem
->xTab
;
1635 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1639 rect
.right
= lpitem
->xTab
;
1640 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1643 if(lpitem
->fState
& MF_GRAYED
)
1645 if (!(lpitem
->fState
& MF_HILITE
) )
1647 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1648 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1649 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1650 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1652 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1654 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1658 SelectObject (hdc
, hfontOld
);
1663 /***********************************************************************
1664 * MENU_DrawPopupMenu
1666 * Paint a popup menu.
1668 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1670 HBRUSH hPrevBrush
= 0;
1673 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1675 GetClientRect( hwnd
, &rect
);
1677 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1678 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1682 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1684 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1688 BOOL flat_menu
= FALSE
;
1690 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1692 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1694 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1696 menu
= MENU_GetMenu( hmenu
);
1698 /* draw menu items */
1699 if (menu
&& menu
->nItems
)
1704 for (u
= menu
->nItems
, item
= menu
->items
; u
> 0; u
--, item
++)
1705 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
, item
,
1706 menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1710 /* draw scroll arrows */
1711 if (menu
->bScrolling
)
1712 MENU_DrawScrollArrows(menu
, hdc
);
1715 SelectObject( hdc
, hPrevBrush
);
1720 /***********************************************************************
1723 * Paint a menu bar. Returns the height of the menu bar.
1724 * called from [windows/nonclient.c]
1726 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1731 HMENU hMenu
= GetMenu(hwnd
);
1733 lppop
= MENU_GetMenu( hMenu
);
1734 if (lppop
== NULL
|| lprect
== NULL
)
1736 return GetSystemMetrics(SM_CYMENU
);
1741 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1743 if (lppop
->Height
== 0)
1744 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1746 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1748 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1749 return lppop
->Height
;
1752 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1756 /***********************************************************************
1759 * Display a popup menu.
1761 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
,
1762 INT x
, INT y
, INT xanchor
, INT yanchor
)
1767 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1768 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1770 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1771 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1773 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1774 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1777 /* store the owner for DrawItem */
1778 menu
->hwndOwner
= hwndOwner
;
1780 menu
->nScrollPos
= 0;
1781 MENU_PopupMenuCalcSize( menu
, hwndOwner
);
1783 /* adjust popup menu pos so that it fits within the desktop */
1785 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1786 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1788 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1791 x
-= width
- xanchor
;
1792 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1793 x
= GetSystemMetrics(SM_CXSCREEN
) - width
;
1797 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1800 y
-= height
+ yanchor
;
1801 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1802 y
= GetSystemMetrics(SM_CYSCREEN
) - height
;
1806 /* NOTE: In Windows, top menu popup is not owned. */
1807 menu
->hWnd
= CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW
, NULL
,
1808 WS_POPUP
, x
, y
, width
, height
,
1809 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1811 if( !menu
->hWnd
) return FALSE
;
1812 if (!top_popup
) top_popup
= menu
->hWnd
;
1814 /* Display the window */
1816 SetWindowPos( menu
->hWnd
, HWND_TOP
, 0, 0, 0, 0,
1817 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1818 UpdateWindow( menu
->hWnd
);
1823 /***********************************************************************
1824 * MENU_EnsureMenuItemVisible
1827 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1829 if (lppop
->bScrolling
)
1831 MENUITEM
*item
= &lppop
->items
[wIndex
];
1832 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1833 UINT nOldPos
= lppop
->nScrollPos
;
1835 UINT arrow_bitmap_height
;
1838 GetClientRect(lppop
->hWnd
, &rc
);
1840 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1841 arrow_bitmap_height
= bmp
.bmHeight
;
1843 rc
.top
+= arrow_bitmap_height
;
1844 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1846 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1847 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1850 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1851 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1852 MENU_DrawScrollArrows(lppop
, hdc
);
1854 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1856 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1857 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1858 MENU_DrawScrollArrows(lppop
, hdc
);
1864 /***********************************************************************
1867 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1868 BOOL sendMenuSelect
, HMENU topmenu
)
1873 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1875 lppop
= MENU_GetMenu( hmenu
);
1876 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1878 if (lppop
->FocusedItem
== wIndex
) return;
1879 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1880 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1881 if (!top_popup
) top_popup
= lppop
->hWnd
;
1883 SelectObject( hdc
, get_menu_font(FALSE
));
1885 /* Clear previous highlighted item */
1886 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1888 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1889 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1890 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1894 /* Highlight new item (if any) */
1895 lppop
->FocusedItem
= wIndex
;
1896 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1898 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1899 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1900 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1901 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1902 &lppop
->items
[wIndex
], lppop
->Height
,
1903 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1907 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1908 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1909 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1910 ip
->fType
| ip
->fState
|
1911 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1914 else if (sendMenuSelect
) {
1917 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1918 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1919 MENUITEM
*ip
= &ptm
->items
[pos
];
1920 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1921 ip
->fType
| ip
->fState
|
1922 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1926 ReleaseDC( lppop
->hWnd
, hdc
);
1930 /***********************************************************************
1931 * MENU_MoveSelection
1933 * Moves currently selected item according to the offset parameter.
1934 * If there is no selection then it should select the last item if
1935 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1937 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1942 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1944 menu
= MENU_GetMenu( hmenu
);
1945 if ((!menu
) || (!menu
->items
)) return;
1947 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1949 if( menu
->nItems
== 1 ) return; else
1950 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1952 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1954 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1959 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1960 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1961 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1963 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1969 /**********************************************************************
1972 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1975 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
1978 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
1979 TRACE("flags=%x str=%p\n", flags
, str
);
1981 if (IS_STRING_ITEM(flags
))
1983 LPWSTR prevText
= item
->text
;
1986 flags
|= MF_SEPARATOR
;
1992 /* Item beginning with a backspace is a help item */
1998 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
2000 strcpyW( text
, str
);
2003 item
->hbmpItem
= NULL
;
2004 HeapFree( GetProcessHeap(), 0, prevText
);
2006 else if(( flags
& MFT_BITMAP
)) {
2007 item
->hbmpItem
= HBITMAP_32(LOWORD(str
));
2008 /* setting bitmap clears text */
2009 HeapFree( GetProcessHeap(), 0, item
->text
);
2013 if (flags
& MF_OWNERDRAW
)
2014 item
->dwItemData
= (DWORD_PTR
)str
;
2016 item
->dwItemData
= 0;
2018 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
2019 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
2021 if (flags
& MF_POPUP
)
2023 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
2024 if (menu
) menu
->wFlags
|= MF_POPUP
;
2036 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
2038 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
2039 flags
|= MF_POPUP
; /* keep popup */
2041 item
->fType
= flags
& TYPE_MASK
;
2042 item
->fState
= (flags
& STATE_MASK
) &
2043 ~(MF_HILITE
| MF_MOUSESELECT
| MF_BYPOSITION
);
2045 /* Don't call SetRectEmpty here! */
2047 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
2052 /**********************************************************************
2055 * Insert (allocate) a new item into a menu.
2057 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2062 if (!(menu
= MENU_GetMenu(hMenu
)))
2065 /* Find where to insert new item */
2067 if (flags
& MF_BYPOSITION
) {
2068 if (pos
> menu
->nItems
)
2071 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2074 if (!(menu
= MENU_GetMenu( hMenu
)))
2079 /* Create new items array */
2081 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2084 WARN("allocation failed\n" );
2087 if (menu
->nItems
> 0)
2089 /* Copy the old array into the new one */
2090 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2091 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2092 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2093 HeapFree( GetProcessHeap(), 0, menu
->items
);
2095 menu
->items
= newItems
;
2097 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2098 menu
->Height
= 0; /* force size recalculate */
2099 return &newItems
[pos
];
2103 /**********************************************************************
2104 * MENU_ParseResource
2106 * Parse a standard menu resource and add items to the menu.
2107 * Return a pointer to the end of the resource.
2109 * NOTE: flags is equivalent to the mtOption field
2111 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2118 flags
= GET_WORD(res
);
2119 res
+= sizeof(WORD
);
2120 if (!(flags
& MF_POPUP
))
2123 res
+= sizeof(WORD
);
2126 if (!unicode
) res
+= strlen(str
) + 1;
2127 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2128 if (flags
& MF_POPUP
)
2130 HMENU hSubMenu
= CreatePopupMenu();
2131 if (!hSubMenu
) return NULL
;
2132 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2134 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2135 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2137 else /* Not a popup */
2139 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2140 else AppendMenuW( hMenu
, flags
, id
,
2141 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2143 } while (!(flags
& MF_END
));
2148 /**********************************************************************
2149 * MENUEX_ParseResource
2151 * Parse an extended menu resource and add items to the menu.
2152 * Return a pointer to the end of the resource.
2154 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2160 mii
.cbSize
= sizeof(mii
);
2161 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2162 mii
.fType
= GET_DWORD(res
);
2163 res
+= sizeof(DWORD
);
2164 mii
.fState
= GET_DWORD(res
);
2165 res
+= sizeof(DWORD
);
2166 mii
.wID
= GET_DWORD(res
);
2167 res
+= sizeof(DWORD
);
2168 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2169 res
+= sizeof(WORD
);
2170 /* Align the text on a word boundary. */
2171 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2172 mii
.dwTypeData
= (LPWSTR
) res
;
2173 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2174 /* Align the following fields on a dword boundary. */
2175 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2177 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2178 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2180 if (resinfo
& 1) { /* Pop-up? */
2181 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2182 res
+= sizeof(DWORD
);
2183 mii
.hSubMenu
= CreatePopupMenu();
2186 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2187 DestroyMenu(mii
.hSubMenu
);
2190 mii
.fMask
|= MIIM_SUBMENU
;
2191 mii
.fType
|= MF_POPUP
;
2193 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2195 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2196 mii
.wID
, mii
.fType
);
2197 mii
.fType
|= MF_SEPARATOR
;
2199 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2200 } while (!(resinfo
& MF_END
));
2205 /***********************************************************************
2208 * Return the handle of the selected sub-popup menu (if any).
2210 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2215 menu
= MENU_GetMenu( hmenu
);
2217 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2219 item
= &menu
->items
[menu
->FocusedItem
];
2220 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2221 return item
->hSubMenu
;
2226 /***********************************************************************
2227 * MENU_HideSubPopups
2229 * Hide the sub-popup menus of this menu.
2231 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2232 BOOL sendMenuSelect
)
2234 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2236 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2238 if (menu
&& top_popup
)
2244 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2246 item
= &menu
->items
[menu
->FocusedItem
];
2247 if (!(item
->fType
& MF_POPUP
) ||
2248 !(item
->fState
& MF_MOUSESELECT
)) return;
2249 item
->fState
&= ~MF_MOUSESELECT
;
2250 hsubmenu
= item
->hSubMenu
;
2253 submenu
= MENU_GetMenu( hsubmenu
);
2254 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
);
2255 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2256 DestroyWindow( submenu
->hWnd
);
2262 /***********************************************************************
2265 * Display the sub-menu of the selected item of this menu.
2266 * Return the handle of the submenu, or hmenu if no submenu to display.
2268 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2269 BOOL selectFirst
, UINT wFlags
)
2276 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2278 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2280 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2282 item
= &menu
->items
[menu
->FocusedItem
];
2283 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2286 /* message must be sent before using item,
2287 because nearly everything may be changed by the application ! */
2289 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2290 if (!(wFlags
& TPM_NONOTIFY
))
2291 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2292 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2294 item
= &menu
->items
[menu
->FocusedItem
];
2297 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2298 if (!(item
->fState
& MF_HILITE
))
2300 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2301 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2303 SelectObject( hdc
, get_menu_font(FALSE
));
2305 item
->fState
|= MF_HILITE
;
2306 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2307 ReleaseDC( menu
->hWnd
, hdc
);
2309 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2312 item
->fState
|= MF_MOUSESELECT
;
2314 if (IS_SYSTEM_MENU(menu
))
2316 MENU_InitSysMenuPopup(item
->hSubMenu
,
2317 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2318 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2320 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2321 rect
.top
= rect
.bottom
;
2322 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2323 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2327 GetWindowRect( menu
->hWnd
, &rect
);
2328 if (menu
->wFlags
& MF_POPUP
)
2330 RECT rc
= item
->rect
;
2332 MENU_AdjustMenuItemRect(menu
, &rc
);
2333 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2335 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2336 rect
.bottom
= rc
.top
- rc
.bottom
;
2340 rect
.left
+= item
->rect
.left
;
2341 rect
.top
+= item
->rect
.bottom
;
2342 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2343 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2347 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
,
2348 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2350 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2351 return item
->hSubMenu
;
2356 /**********************************************************************
2359 HWND
MENU_IsMenuActive(void)
2364 /***********************************************************************
2367 * Walks menu chain trying to find a menu pt maps to.
2369 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2371 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2372 UINT item
= menu
->FocusedItem
;
2375 /* try subpopup first (if any) */
2376 ret
= (item
!= NO_SELECTED_ITEM
&&
2377 (menu
->items
[item
].fType
& MF_POPUP
) &&
2378 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2379 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2381 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2383 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2384 if( menu
->wFlags
& MF_POPUP
)
2386 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2388 else if (ht
== HTSYSMENU
)
2389 ret
= get_win_sys_menu( menu
->hWnd
);
2390 else if (ht
== HTMENU
)
2391 ret
= GetMenu( menu
->hWnd
);
2396 /***********************************************************************
2397 * MENU_ExecFocusedItem
2399 * Execute a menu item (for instance when user pressed Enter).
2400 * Return the wID of the executed item. Otherwise, -1 indicating
2401 * that no menu item was executed;
2402 * Have to receive the flags for the TrackPopupMenu options to avoid
2403 * sending unwanted message.
2406 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2409 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2411 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2413 if (!menu
|| !menu
->nItems
||
2414 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2416 item
= &menu
->items
[menu
->FocusedItem
];
2418 TRACE("%p %08x %p\n", hMenu
, item
->wID
, item
->hSubMenu
);
2420 if (!(item
->fType
& MF_POPUP
))
2422 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2424 /* If TPM_RETURNCMD is set you return the id, but
2425 do not send a message to the owner */
2426 if(!(wFlags
& TPM_RETURNCMD
))
2428 if( menu
->wFlags
& MF_SYSMENU
)
2429 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2430 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2432 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2438 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2443 /***********************************************************************
2444 * MENU_SwitchTracking
2446 * Helper function for menu navigation routines.
2448 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
)
2450 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2451 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2453 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2455 if( pmt
->hTopMenu
!= hPtMenu
&&
2456 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2458 /* both are top level menus (system and menu-bar) */
2459 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2460 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2461 pmt
->hTopMenu
= hPtMenu
;
2463 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
);
2464 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2468 /***********************************************************************
2471 * Return TRUE if we can go on with menu tracking.
2473 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2475 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2480 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2483 if( IS_SYSTEM_MENU(ptmenu
) )
2484 item
= ptmenu
->items
;
2486 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2490 if( ptmenu
->FocusedItem
!= id
)
2491 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2493 /* If the popup menu is not already "popped" */
2494 if(!(item
->fState
& MF_MOUSESELECT
))
2496 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2501 /* Else the click was on the menu bar, finish the tracking */
2506 /***********************************************************************
2509 * Return the value of MENU_ExecFocusedItem if
2510 * the selected item was not a popup. Else open the popup.
2511 * A -1 return value indicates that we go on with menu tracking.
2514 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2516 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2521 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2524 if( IS_SYSTEM_MENU(ptmenu
) )
2525 item
= ptmenu
->items
;
2527 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2529 if( item
&& (ptmenu
->FocusedItem
== id
))
2531 if( !(item
->fType
& MF_POPUP
) )
2532 return MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2534 /* If we are dealing with the top-level menu */
2535 /* and this is a click on an already "popped" item: */
2536 /* Stop the menu tracking and close the opened submenus */
2537 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2540 ptmenu
->bTimeToHide
= TRUE
;
2546 /***********************************************************************
2549 * Return TRUE if we can go on with menu tracking.
2551 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2553 UINT id
= NO_SELECTED_ITEM
;
2554 POPUPMENU
*ptmenu
= NULL
;
2558 ptmenu
= MENU_GetMenu( hPtMenu
);
2559 if( IS_SYSTEM_MENU(ptmenu
) )
2562 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2565 if( id
== NO_SELECTED_ITEM
)
2567 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2568 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2571 else if( ptmenu
->FocusedItem
!= id
)
2573 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2574 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2580 /***********************************************************************
2583 static void MENU_SetCapture( HWND hwnd
)
2587 SERVER_START_REQ( set_capture_window
)
2590 req
->flags
= CAPTURE_MENU
;
2591 if (!wine_server_call_err( req
))
2593 previous
= reply
->previous
;
2594 hwnd
= reply
->full_handle
;
2599 if (previous
&& previous
!= hwnd
)
2600 SendMessageW( previous
, WM_CAPTURECHANGED
, 0, (LPARAM
)hwnd
);
2604 /***********************************************************************
2607 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2609 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
)
2611 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2613 if( (vk
== VK_LEFT
&& menu
->FocusedItem
== 0 ) ||
2614 (vk
== VK_RIGHT
&& menu
->FocusedItem
== menu
->nItems
- 1))
2616 MDINEXTMENU next_menu
;
2621 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2622 next_menu
.hmenuNext
= 0;
2623 next_menu
.hwndNext
= 0;
2624 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2626 TRACE("%p [%p] -> %p [%p]\n",
2627 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2629 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2631 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2632 hNewWnd
= pmt
->hOwnerWnd
;
2633 if( IS_SYSTEM_MENU(menu
) )
2635 /* switch to the menu bar */
2637 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2641 menu
= MENU_GetMenu( hNewMenu
);
2642 id
= menu
->nItems
- 1;
2645 else if (style
& WS_SYSMENU
)
2647 /* switch to the system menu */
2648 hNewMenu
= get_win_sys_menu( hNewWnd
);
2652 else /* application returned a new menu to switch to */
2654 hNewMenu
= next_menu
.hmenuNext
;
2655 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2657 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2659 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2661 if (style
& WS_SYSMENU
&&
2662 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2664 /* get the real system menu */
2665 hNewMenu
= get_win_sys_menu(hNewWnd
);
2667 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2669 /* FIXME: Not sure what to do here;
2670 * perhaps try to track hNewMenu as a popup? */
2672 TRACE(" -- got confused.\n");
2679 if( hNewMenu
!= pmt
->hTopMenu
)
2681 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2683 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2684 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2687 if( hNewWnd
!= pmt
->hOwnerWnd
)
2689 pmt
->hOwnerWnd
= hNewWnd
;
2690 MENU_SetCapture( pmt
->hOwnerWnd
);
2693 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2694 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2701 /***********************************************************************
2704 * The idea is not to show the popup if the next input message is
2705 * going to hide it anyway.
2707 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2711 msg
.hwnd
= pmt
->hOwnerWnd
;
2713 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2714 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2719 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2720 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2722 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2723 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2724 if( msg
.message
== WM_KEYDOWN
&&
2725 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2727 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2734 /* failures go through this */
2735 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2739 /***********************************************************************
2742 * Handle a VK_ESCAPE key event in a menu.
2744 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2746 BOOL bEndMenu
= TRUE
;
2748 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2750 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2752 if (menu
->wFlags
& MF_POPUP
)
2754 HMENU hmenutmp
, hmenuprev
;
2756 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2758 /* close topmost popup */
2759 while (hmenutmp
!= pmt
->hCurrentMenu
)
2761 hmenuprev
= hmenutmp
;
2762 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2765 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2766 pmt
->hCurrentMenu
= hmenuprev
;
2774 /***********************************************************************
2777 * Handle a VK_LEFT key event in a menu.
2779 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2782 HMENU hmenutmp
, hmenuprev
;
2785 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2786 menu
= MENU_GetMenu( hmenutmp
);
2788 /* Try to move 1 column left (if possible) */
2789 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2790 NO_SELECTED_ITEM
) {
2792 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2797 /* close topmost popup */
2798 while (hmenutmp
!= pmt
->hCurrentMenu
)
2800 hmenuprev
= hmenutmp
;
2801 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2804 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2805 pmt
->hCurrentMenu
= hmenuprev
;
2807 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2809 /* move menu bar selection if no more popups are left */
2811 if( !MENU_DoNextMenu( pmt
, VK_LEFT
) )
2812 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2814 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2816 /* A sublevel menu was displayed - display the next one
2817 * unless there is another displacement coming up */
2819 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2820 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2821 pmt
->hTopMenu
, TRUE
, wFlags
);
2827 /***********************************************************************
2830 * Handle a VK_RIGHT key event in a menu.
2832 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2835 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2838 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2840 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2841 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2843 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2845 /* If already displaying a popup, try to display sub-popup */
2847 hmenutmp
= pmt
->hCurrentMenu
;
2848 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2850 /* if subpopup was displayed then we are done */
2851 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2854 /* Check to see if there's another column */
2855 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2856 NO_SELECTED_ITEM
) {
2857 TRACE("Going to %d.\n", nextcol
);
2858 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2863 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2865 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2867 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2868 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2869 } else hmenutmp
= 0;
2871 /* try to move to the next item */
2872 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
) )
2873 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2875 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2876 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2877 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2878 pmt
->hTopMenu
, TRUE
, wFlags
);
2882 /***********************************************************************
2885 * Menu tracking code.
2887 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2888 HWND hwnd
, const RECT
*lprect
)
2893 INT executedMenuId
= -1;
2895 BOOL enterIdleSent
= FALSE
;
2898 mt
.hCurrentMenu
= hmenu
;
2899 mt
.hTopMenu
= hmenu
;
2900 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2904 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2905 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2908 if (!(menu
= MENU_GetMenu( hmenu
)))
2910 WARN("Invalid menu handle %p\n", hmenu
);
2911 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2915 if (wFlags
& TPM_BUTTONDOWN
)
2917 /* Get the result in order to start the tracking or not */
2918 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2919 fEndMenu
= !fRemove
;
2922 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
2924 MENU_SetCapture( mt
.hOwnerWnd
);
2928 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2929 if (!menu
) /* sometimes happens if I do a window manager close */
2932 /* we have to keep the message in the queue until it's
2933 * clear that menu loop is not over yet. */
2937 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
2939 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
2940 /* remove the message from the queue */
2941 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2947 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
2948 enterIdleSent
= TRUE
;
2949 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
2955 /* check if EndMenu() tried to cancel us, by posting this message */
2956 if(msg
.message
== WM_CANCELMODE
)
2958 /* we are now out of the loop */
2961 /* remove the message from the queue */
2962 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2964 /* break out of internal loop, ala ESCAPE */
2968 TranslateMessage( &msg
);
2971 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
2972 enterIdleSent
=FALSE
;
2975 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
2978 * Use the mouse coordinates in lParam instead of those in the MSG
2979 * struct to properly handle synthetic messages. They are already
2980 * in screen coordinates.
2982 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
2983 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
2985 /* Find a menu for this mouse event */
2986 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
2990 /* no WM_NC... messages in captured state */
2992 case WM_RBUTTONDBLCLK
:
2993 case WM_RBUTTONDOWN
:
2994 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2996 case WM_LBUTTONDBLCLK
:
2997 case WM_LBUTTONDOWN
:
2998 /* If the message belongs to the menu, removes it from the queue */
2999 /* Else, end menu tracking */
3000 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3001 fEndMenu
= !fRemove
;
3005 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3008 /* Check if a menu was selected by the mouse */
3011 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3013 /* End the loop if executedMenuId is an item ID */
3014 /* or if the job was done (executedMenuId = 0). */
3015 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3017 /* No menu was selected by the mouse */
3018 /* if the function was called by TrackPopupMenu, continue
3019 with the menu tracking. If not, stop it */
3021 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3026 /* the selected menu item must be changed every time */
3027 /* the mouse moves. */
3030 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3032 } /* switch(msg.message) - mouse */
3034 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3036 fRemove
= TRUE
; /* Keyboard messages are always removed */
3049 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3050 NO_SELECTED_ITEM
, FALSE
, 0 );
3051 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3052 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3056 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3058 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3059 if (!(menu
->wFlags
& MF_POPUP
))
3060 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3061 else /* otherwise try to move selection */
3062 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3063 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3067 MENU_KeyLeft( &mt
, wFlags
);
3071 MENU_KeyRight( &mt
, wFlags
);
3075 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3081 hi
.cbSize
= sizeof(HELPINFO
);
3082 hi
.iContextType
= HELPINFO_MENUITEM
;
3083 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3086 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3087 hi
.hItemHandle
= hmenu
;
3088 hi
.dwContextId
= menu
->dwContextHelpID
;
3089 hi
.MousePos
= msg
.pt
;
3090 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3097 break; /* WM_KEYDOWN */
3104 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3106 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3107 fEndMenu
= (executedMenuId
!= -1);
3112 /* Hack to avoid control chars. */
3113 /* We will find a better way real soon... */
3114 if (msg
.wParam
< 32) break;
3116 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3117 LOWORD(msg
.wParam
), FALSE
);
3118 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3119 else if (pos
== (UINT
)-1) MessageBeep(0);
3122 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3124 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3125 fEndMenu
= (executedMenuId
!= -1);
3129 } /* switch(msg.message) - kbd */
3133 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3134 DispatchMessageW( &msg
);
3138 if (!fEndMenu
) fRemove
= TRUE
;
3140 /* finally remove message from the queue */
3142 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3143 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3144 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3147 MENU_SetCapture(0); /* release the capture */
3149 /* If dropdown is still painted and the close box is clicked on
3150 then the menu will be destroyed as part of the DispatchMessage above.
3151 This will then invalidate the menu handle in mt.hTopMenu. We should
3152 check for this first. */
3153 if( IsMenu( mt
.hTopMenu
) )
3155 menu
= MENU_GetMenu( mt
.hTopMenu
);
3157 if( IsWindow( mt
.hOwnerWnd
) )
3159 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
);
3161 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3163 DestroyWindow( menu
->hWnd
);
3166 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3167 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
3170 /* Reset the variable for hiding menu */
3171 if( menu
) menu
->bTimeToHide
= FALSE
;
3174 /* The return value is only used by TrackPopupMenu */
3175 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3176 if (executedMenuId
== -1) executedMenuId
= 0;
3177 return executedMenuId
;
3180 /***********************************************************************
3183 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3187 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3191 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3192 if (!(wFlags
& TPM_NONOTIFY
))
3193 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3195 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3197 if (!(wFlags
& TPM_NONOTIFY
))
3199 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3200 /* If an app changed/recreated menu bar entries in WM_INITMENU
3201 * menu sizes will be recalculated once the menu created/shown.
3205 /* This makes the menus of applications built with Delphi work.
3206 * It also enables menus to be displayed in more than one window,
3207 * but there are some bugs left that need to be fixed in this case.
3209 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3213 /***********************************************************************
3216 static BOOL
MENU_ExitTracking(HWND hWnd
)
3218 TRACE("hwnd=%p\n", hWnd
);
3220 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3226 /***********************************************************************
3227 * MENU_TrackMouseMenuBar
3229 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3231 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3233 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3234 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3236 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3240 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3241 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3242 MENU_ExitTracking(hWnd
);
3247 /***********************************************************************
3248 * MENU_TrackKbdMenuBar
3250 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3252 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3254 UINT uItem
= NO_SELECTED_ITEM
;
3256 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3258 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3260 /* find window that has a menu */
3262 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3263 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3265 /* check if we have to track a system menu */
3267 hTrackMenu
= GetMenu( hwnd
);
3268 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3270 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3271 hTrackMenu
= get_win_sys_menu( hwnd
);
3273 wParam
|= HTSYSMENU
; /* prevent item lookup */
3276 if (!IsMenu( hTrackMenu
)) return;
3278 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3280 if( wChar
&& wChar
!= ' ' )
3282 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3283 if ( uItem
>= (UINT
)(-2) )
3285 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3286 /* schedule end of menu tracking */
3287 wFlags
|= TF_ENDMENU
;
3292 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3294 if (wParam
& HTSYSMENU
)
3296 /* prevent sysmenu activation for managed windows on Alt down/up */
3297 if (GetPropA( hwnd
, "__wine_x11_managed" ))
3298 wFlags
|= TF_ENDMENU
; /* schedule end of menu tracking */
3302 if( uItem
== NO_SELECTED_ITEM
)
3303 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3305 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3309 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3310 MENU_ExitTracking( hwnd
);
3314 /**********************************************************************
3315 * TrackPopupMenu (USER32.@)
3317 * Like the win32 API, the function return the command ID only if the
3318 * flag TPM_RETURNCMD is on.
3321 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3322 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3326 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3328 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3329 if (!(wFlags
& TPM_NONOTIFY
))
3330 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3332 if (MENU_ShowPopup( hWnd
, hMenu
, 0, x
, y
, 0, 0 ))
3333 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3334 MENU_ExitTracking(hWnd
);
3339 /**********************************************************************
3340 * TrackPopupMenuEx (USER32.@)
3342 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3343 HWND hWnd
, LPTPMPARAMS lpTpm
)
3345 FIXME("not fully implemented\n" );
3346 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3347 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3350 /***********************************************************************
3353 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3355 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3357 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3363 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3364 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3368 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3369 return MA_NOACTIVATE
;
3374 BeginPaint( hwnd
, &ps
);
3375 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3376 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3377 EndPaint( hwnd
, &ps
);
3384 /* zero out global pointer in case resident popup window was destroyed. */
3385 if (hwnd
== top_popup
) top_popup
= 0;
3392 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3395 SetWindowLongPtrW( hwnd
, 0, 0 );
3398 case MM_SETMENUHANDLE
:
3399 SetWindowLongPtrW( hwnd
, 0, wParam
);
3402 case MM_GETMENUHANDLE
:
3403 return GetWindowLongPtrW( hwnd
, 0 );
3406 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3412 /***********************************************************************
3413 * MENU_GetMenuBarHeight
3415 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3417 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3418 INT orgX
, INT orgY
)
3424 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3426 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3428 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3429 SelectObject( hdc
, get_menu_font(FALSE
));
3430 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3431 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3432 ReleaseDC( hwnd
, hdc
);
3433 return lppop
->Height
;
3437 /*******************************************************************
3438 * ChangeMenuA (USER32.@)
3440 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3441 UINT id
, UINT flags
)
3443 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3444 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3446 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3447 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3449 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3450 flags
& MF_BYPOSITION
? pos
: id
,
3451 flags
& ~MF_REMOVE
);
3452 /* Default: MF_INSERT */
3453 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3457 /*******************************************************************
3458 * ChangeMenuW (USER32.@)
3460 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3461 UINT id
, UINT flags
)
3463 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3464 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3466 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3467 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3469 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3470 flags
& MF_BYPOSITION
? pos
: id
,
3471 flags
& ~MF_REMOVE
);
3472 /* Default: MF_INSERT */
3473 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3477 /*******************************************************************
3478 * CheckMenuItem (USER32.@)
3480 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3485 TRACE("menu=%p id=%04x flags=%04x\n", hMenu
, id
, flags
);
3486 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3487 ret
= item
->fState
& MF_CHECKED
;
3488 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3489 else item
->fState
&= ~MF_CHECKED
;
3494 /**********************************************************************
3495 * EnableMenuItem (USER32.@)
3497 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3503 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3505 /* Get the Popupmenu to access the owner menu */
3506 if (!(menu
= MENU_GetMenu(hMenu
)))
3509 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3512 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3513 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3515 /* If the close item in the system menu change update the close button */
3516 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3518 if (menu
->hSysMenuOwner
!= 0)
3521 POPUPMENU
* parentMenu
;
3523 /* Get the parent menu to access*/
3524 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3527 /* Refresh the frame to reflect the change */
3528 GetWindowRect(parentMenu
->hWnd
, &rc
);
3529 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3531 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3539 /*******************************************************************
3540 * GetMenuStringA (USER32.@)
3542 INT WINAPI
GetMenuStringA(
3543 HMENU hMenu
, /* [in] menuhandle */
3544 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3545 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3546 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3547 UINT wFlags
/* [in] MF_ flags */
3551 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3552 if (str
&& nMaxSiz
) str
[0] = '\0';
3553 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3554 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3557 if (!item
->text
) return 0;
3558 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3559 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3561 TRACE("returning '%s'\n", str
);
3566 /*******************************************************************
3567 * GetMenuStringW (USER32.@)
3569 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3570 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3574 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3575 if (str
&& nMaxSiz
) str
[0] = '\0';
3576 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3577 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3580 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3581 if( !(item
->text
)) {
3585 lstrcpynW( str
, item
->text
, nMaxSiz
);
3586 return strlenW(str
);
3590 /**********************************************************************
3591 * HiliteMenuItem (USER32.@)
3593 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3597 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3598 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3599 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3600 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3601 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
);
3602 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3607 /**********************************************************************
3608 * GetMenuState (USER32.@)
3610 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3613 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3614 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3615 debug_print_menuitem (" item: ", item
, "");
3616 if (item
->fType
& MF_POPUP
)
3618 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3619 if (!menu
) return -1;
3620 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3624 /* We used to (from way back then) mask the result to 0xff. */
3625 /* I don't know why and it seems wrong as the documented */
3626 /* return flag MF_SEPARATOR is outside that mask. */
3627 return (item
->fType
| item
->fState
);
3632 /**********************************************************************
3633 * GetMenuItemCount (USER32.@)
3635 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3637 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3638 if (!menu
) return -1;
3639 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3640 return menu
->nItems
;
3644 /**********************************************************************
3645 * GetMenuItemID (USER32.@)
3647 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3651 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3652 if (lpmi
->fType
& MF_POPUP
) return -1;
3658 /*******************************************************************
3659 * InsertMenuW (USER32.@)
3661 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3662 UINT_PTR id
, LPCWSTR str
)
3666 if (IS_STRING_ITEM(flags
) && str
)
3667 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3668 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3669 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %p (not a string)\n",
3670 hMenu
, pos
, flags
, id
, str
);
3672 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3674 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3676 RemoveMenu( hMenu
, pos
, flags
);
3680 if (flags
& MF_POPUP
) /* Set the MF_POPUP flag on the popup-menu */
3681 (MENU_GetMenu((HMENU
)id
))->wFlags
|= MF_POPUP
;
3683 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3688 /*******************************************************************
3689 * InsertMenuA (USER32.@)
3691 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3692 UINT_PTR id
, LPCSTR str
)
3696 if (IS_STRING_ITEM(flags
) && str
)
3698 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3699 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3702 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3703 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3704 HeapFree( GetProcessHeap(), 0, newstr
);
3708 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3712 /*******************************************************************
3713 * AppendMenuA (USER32.@)
3715 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3716 UINT_PTR id
, LPCSTR data
)
3718 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3722 /*******************************************************************
3723 * AppendMenuW (USER32.@)
3725 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3726 UINT_PTR id
, LPCWSTR data
)
3728 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3732 /**********************************************************************
3733 * RemoveMenu (USER32.@)
3735 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3740 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3741 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3742 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3746 MENU_FreeItemData( item
);
3748 if (--menu
->nItems
== 0)
3750 HeapFree( GetProcessHeap(), 0, menu
->items
);
3755 while(nPos
< menu
->nItems
)
3761 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3762 menu
->nItems
* sizeof(MENUITEM
) );
3768 /**********************************************************************
3769 * DeleteMenu (USER32.@)
3771 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3773 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3774 if (!item
) return FALSE
;
3775 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3776 /* nPos is now the position of the item */
3777 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3782 /*******************************************************************
3783 * ModifyMenuW (USER32.@)
3785 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3786 UINT_PTR id
, LPCWSTR str
)
3790 if (IS_STRING_ITEM(flags
))
3791 TRACE("%p %d %04x %04x %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3793 TRACE("%p %d %04x %04x %p\n", hMenu
, pos
, flags
, id
, str
);
3795 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3796 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3797 return MENU_SetItemData( item
, flags
, id
, str
);
3801 /*******************************************************************
3802 * ModifyMenuA (USER32.@)
3804 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3805 UINT_PTR id
, LPCSTR str
)
3809 if (IS_STRING_ITEM(flags
) && str
)
3811 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3812 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3815 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3816 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3817 HeapFree( GetProcessHeap(), 0, newstr
);
3821 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3825 /**********************************************************************
3826 * CreatePopupMenu (USER32.@)
3828 HMENU WINAPI
CreatePopupMenu(void)
3833 if (!(hmenu
= CreateMenu())) return 0;
3834 menu
= MENU_GetMenu( hmenu
);
3835 menu
->wFlags
|= MF_POPUP
;
3836 menu
->bTimeToHide
= FALSE
;
3841 /**********************************************************************
3842 * GetMenuCheckMarkDimensions (USER.417)
3843 * GetMenuCheckMarkDimensions (USER32.@)
3845 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3847 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3851 /**********************************************************************
3852 * SetMenuItemBitmaps (USER32.@)
3854 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3855 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3858 TRACE("(%p, %04x, %04x, %p, %p)\n",
3859 hMenu
, nPos
, wFlags
, hNewCheck
, hNewUnCheck
);
3860 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3862 if (!hNewCheck
&& !hNewUnCheck
)
3864 item
->fState
&= ~MF_USECHECKBITMAPS
;
3866 else /* Install new bitmaps */
3868 item
->hCheckBit
= hNewCheck
;
3869 item
->hUnCheckBit
= hNewUnCheck
;
3870 item
->fState
|= MF_USECHECKBITMAPS
;
3876 /**********************************************************************
3877 * CreateMenu (USER32.@)
3879 HMENU WINAPI
CreateMenu(void)
3883 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3884 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3886 ZeroMemory(menu
, sizeof(POPUPMENU
));
3887 menu
->wMagic
= MENU_MAGIC
;
3888 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3889 menu
->bTimeToHide
= FALSE
;
3891 TRACE("return %p\n", hMenu
);
3897 /**********************************************************************
3898 * DestroyMenu (USER32.@)
3900 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3902 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3904 TRACE("(%p)\n", hMenu
);
3907 if (!lppop
) return FALSE
;
3909 lppop
->wMagic
= 0; /* Mark it as destroyed */
3911 /* DestroyMenu should not destroy system menu popup owner */
3912 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
3914 DestroyWindow( lppop
->hWnd
);
3918 if (lppop
->items
) /* recursively destroy submenus */
3921 MENUITEM
*item
= lppop
->items
;
3922 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
3924 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
3925 MENU_FreeItemData( item
);
3927 HeapFree( GetProcessHeap(), 0, lppop
->items
);
3929 USER_HEAP_FREE( hMenu
);
3934 /**********************************************************************
3935 * GetSystemMenu (USER32.@)
3937 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
3939 WND
*wndPtr
= WIN_GetPtr( hWnd
);
3942 if (wndPtr
== WND_DESKTOP
) return 0;
3943 if (wndPtr
== WND_OTHER_PROCESS
)
3945 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
3949 if (wndPtr
->hSysMenu
&& bRevert
)
3951 DestroyMenu(wndPtr
->hSysMenu
);
3952 wndPtr
->hSysMenu
= 0;
3955 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
3956 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
3958 if( wndPtr
->hSysMenu
)
3961 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
3963 /* Store the dummy sysmenu handle to facilitate the refresh */
3964 /* of the close button if the SC_CLOSE item change */
3965 menu
= MENU_GetMenu(retvalue
);
3967 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
3969 WIN_ReleasePtr( wndPtr
);
3971 return bRevert
? 0 : retvalue
;
3975 /*******************************************************************
3976 * SetSystemMenu (USER32.@)
3978 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
3980 WND
*wndPtr
= WIN_GetPtr( hwnd
);
3982 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
3984 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
3985 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
3986 WIN_ReleasePtr( wndPtr
);
3993 /**********************************************************************
3994 * GetMenu (USER32.@)
3996 HMENU WINAPI
GetMenu( HWND hWnd
)
3998 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
3999 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4003 /**********************************************************************
4004 * GetMenuBarInfo (USER32.@)
4006 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4008 FIXME( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4012 /**********************************************************************
4015 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4016 * SetWindowPos call that would result if SetMenu were called directly.
4018 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4020 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4022 if (hMenu
&& !IsMenu(hMenu
))
4024 WARN("hMenu %p is not a menu handle\n", hMenu
);
4025 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4028 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4031 hWnd
= WIN_GetFullHandle( hWnd
);
4032 if (GetCapture() == hWnd
) MENU_SetCapture(0); /* release the capture */
4038 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4040 lpmenu
->hWnd
= hWnd
;
4041 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4043 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4048 /**********************************************************************
4049 * SetMenu (USER32.@)
4051 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4053 if(!MENU_SetMenu(hWnd
, hMenu
))
4056 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4057 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4062 /**********************************************************************
4063 * GetSubMenu (USER32.@)
4065 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4069 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4070 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4071 return lpmi
->hSubMenu
;
4075 /**********************************************************************
4076 * DrawMenuBar (USER32.@)
4078 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4081 HMENU hMenu
= GetMenu(hWnd
);
4083 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4085 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4087 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4088 lppop
->hwndOwner
= hWnd
;
4089 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4090 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4094 /***********************************************************************
4095 * DrawMenuBarTemp (USER32.@)
4099 * called by W98SE desk.cpl Control Panel Applet
4101 * Not 100% sure about the param names, but close.
4103 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4108 BOOL flat_menu
= FALSE
;
4110 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4113 hMenu
= GetMenu(hwnd
);
4116 hFont
= get_menu_font(FALSE
);
4118 lppop
= MENU_GetMenu( hMenu
);
4119 if (lppop
== NULL
|| lprect
== NULL
)
4121 retvalue
= GetSystemMetrics(SM_CYMENU
);
4125 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4127 hfontOld
= SelectObject( hDC
, hFont
);
4129 if (lppop
->Height
== 0)
4130 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4132 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4134 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4136 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4137 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4138 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4140 if (lppop
->nItems
== 0)
4142 retvalue
= GetSystemMetrics(SM_CYMENU
);
4146 for (i
= 0; i
< lppop
->nItems
; i
++)
4148 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4149 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4151 retvalue
= lppop
->Height
;
4154 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4158 /***********************************************************************
4159 * EndMenu (USER.187)
4160 * EndMenu (USER32.@)
4162 void WINAPI
EndMenu(void)
4164 /* if we are in the menu code, and it is active */
4165 if (!fEndMenu
&& top_popup
)
4167 /* terminate the menu handling code */
4170 /* needs to be posted to wakeup the internal menu handler */
4171 /* which will now terminate the menu, in the event that */
4172 /* the main window was minimized, or lost focus, so we */
4173 /* don't end up with an orphaned menu */
4174 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4179 /***********************************************************************
4180 * LookupMenuHandle (USER.217)
4182 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4184 HMENU hmenu32
= HMENU_32(hmenu
);
4186 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4187 else return HMENU_16(hmenu32
);
4191 /**********************************************************************
4192 * LoadMenu (USER.150)
4194 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4200 if (HIWORD(name
) && name
[0] == '#') name
= (LPCSTR
)atoi( name
+ 1 );
4201 if (!name
) return 0;
4203 instance
= GetExePtr( instance
);
4204 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4205 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4206 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4207 FreeResource16( handle
);
4212 /*****************************************************************
4213 * LoadMenuA (USER32.@)
4215 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4217 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4218 if (!hrsrc
) return 0;
4219 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
4223 /*****************************************************************
4224 * LoadMenuW (USER32.@)
4226 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4228 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4229 if (!hrsrc
) return 0;
4230 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
4234 /**********************************************************************
4235 * LoadMenuIndirect (USER.220)
4237 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4240 WORD version
, offset
;
4241 LPCSTR p
= (LPCSTR
)template;
4243 TRACE("(%p)\n", template );
4244 version
= GET_WORD(p
);
4248 WARN("version must be 0 for Win16\n" );
4251 offset
= GET_WORD(p
);
4252 p
+= sizeof(WORD
) + offset
;
4253 if (!(hMenu
= CreateMenu())) return 0;
4254 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4256 DestroyMenu( hMenu
);
4259 return HMENU_16(hMenu
);
4263 /**********************************************************************
4264 * LoadMenuIndirectW (USER32.@)
4266 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4269 WORD version
, offset
;
4270 LPCSTR p
= (LPCSTR
)template;
4272 version
= GET_WORD(p
);
4274 TRACE("%p, ver %d\n", template, version
);
4277 case 0: /* standard format is version of 0 */
4278 offset
= GET_WORD(p
);
4279 p
+= sizeof(WORD
) + offset
;
4280 if (!(hMenu
= CreateMenu())) return 0;
4281 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4283 DestroyMenu( hMenu
);
4287 case 1: /* extended format is version of 1 */
4288 offset
= GET_WORD(p
);
4289 p
+= sizeof(WORD
) + offset
;
4290 if (!(hMenu
= CreateMenu())) return 0;
4291 if (!MENUEX_ParseResource( p
, hMenu
))
4293 DestroyMenu( hMenu
);
4298 ERR("version %d not supported.\n", version
);
4304 /**********************************************************************
4305 * LoadMenuIndirectA (USER32.@)
4307 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4309 return LoadMenuIndirectW( template );
4313 /**********************************************************************
4316 BOOL WINAPI
IsMenu(HMENU hmenu
)
4318 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4319 return menu
!= NULL
;
4322 /**********************************************************************
4323 * GetMenuItemInfo_common
4326 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4327 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4329 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4331 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4336 if( lpmii
->fMask
& MIIM_TYPE
) {
4337 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4338 WARN("invalid combination of fMask bits used\n");
4339 /* this does not happen on Win9x/ME */
4340 SetLastError( ERROR_INVALID_PARAMETER
);
4343 lpmii
->fType
= menu
->fType
& ~MF_POPUP
;
4344 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4345 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4346 if( lpmii
->fType
& MFT_BITMAP
) {
4347 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4349 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4350 /* this does not happen on Win9x/ME */
4351 lpmii
->dwTypeData
= 0;
4356 /* copy the text string */
4357 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4359 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4362 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4364 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4370 len
= strlenW(menu
->text
);
4371 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4372 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4376 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4377 0, NULL
, NULL
) - 1;
4378 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4379 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4380 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4381 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4383 /* if we've copied a substring we return its length */
4384 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4385 if (lpmii
->cch
<= len
+ 1)
4390 /* return length of string */
4391 /* not on Win9x/ME if fType & MFT_BITMAP */
4397 if (lpmii
->fMask
& MIIM_FTYPE
)
4398 lpmii
->fType
= menu
->fType
& ~MF_POPUP
;
4400 if (lpmii
->fMask
& MIIM_BITMAP
)
4401 lpmii
->hbmpItem
= menu
->hbmpItem
;
4403 if (lpmii
->fMask
& MIIM_STATE
)
4404 lpmii
->fState
= menu
->fState
;
4406 if (lpmii
->fMask
& MIIM_ID
)
4407 lpmii
->wID
= menu
->wID
;
4409 if (lpmii
->fMask
& MIIM_SUBMENU
)
4410 lpmii
->hSubMenu
= menu
->hSubMenu
;
4412 /* hSubMenu is always cleared
4413 * (not on Win9x/ME ) */
4414 lpmii
->hSubMenu
= 0;
4417 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4418 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4419 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4421 if (lpmii
->fMask
& MIIM_DATA
)
4422 lpmii
->dwItemData
= menu
->dwItemData
;
4427 /**********************************************************************
4428 * GetMenuItemInfoA (USER32.@)
4430 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4431 LPMENUITEMINFOA lpmii
)
4435 if( lpmii
->cbSize
!= sizeof( mii
) &&
4436 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4437 SetLastError( ERROR_INVALID_PARAMETER
);
4440 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4441 mii
.cbSize
= sizeof( mii
);
4442 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4443 (LPMENUITEMINFOW
)&mii
, FALSE
);
4444 mii
.cbSize
= lpmii
->cbSize
;
4445 memcpy( lpmii
, &mii
, mii
.cbSize
);
4449 /**********************************************************************
4450 * GetMenuItemInfoW (USER32.@)
4452 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4453 LPMENUITEMINFOW lpmii
)
4457 if( lpmii
->cbSize
!= sizeof( mii
) &&
4458 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4459 SetLastError( ERROR_INVALID_PARAMETER
);
4462 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4463 mii
.cbSize
= sizeof( mii
);
4464 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4465 mii
.cbSize
= lpmii
->cbSize
;
4466 memcpy( lpmii
, &mii
, mii
.cbSize
);
4471 /* set a menu item text from a ASCII or Unicode string */
4472 inline static void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4478 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4479 strcpyW( menu
->text
, text
);
4483 LPCSTR str
= (LPCSTR
)text
;
4484 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4485 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4486 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4491 /**********************************************************************
4492 * SetMenuItemInfo_common
4495 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4496 const MENUITEMINFOW
*lpmii
,
4499 if (!menu
) return FALSE
;
4501 debug_print_menuitem("SetmenuItemInfo_common from: ", menu
, "");
4503 if (lpmii
->fMask
& MIIM_TYPE
) {
4504 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4505 WARN("invalid combination of fMask bits used\n");
4506 /* this does not happen on Win9x/ME */
4507 SetLastError( ERROR_INVALID_PARAMETER
);
4510 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4511 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4512 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4514 if (IS_STRING_ITEM(menu
->fType
)) {
4515 HeapFree(GetProcessHeap(), 0, menu
->text
);
4516 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4517 } else if( (menu
->fType
) & MFT_BITMAP
)
4518 menu
->hbmpItem
= (HBITMAP
)lpmii
->dwTypeData
;
4521 if (lpmii
->fMask
& MIIM_FTYPE
) {
4522 if(( lpmii
->fType
& MFT_BITMAP
)) {
4523 SetLastError( ERROR_INVALID_PARAMETER
);
4526 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4527 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4529 if (lpmii
->fMask
& MIIM_STRING
) {
4530 /* free the string when used */
4531 HeapFree(GetProcessHeap(), 0, menu
->text
);
4532 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4535 if (lpmii
->fMask
& MIIM_STATE
)
4537 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4538 menu
->fState
= lpmii
->fState
;
4541 if (lpmii
->fMask
& MIIM_ID
)
4542 menu
->wID
= lpmii
->wID
;
4544 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4545 menu
->hSubMenu
= lpmii
->hSubMenu
;
4546 if (menu
->hSubMenu
) {
4547 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4549 subMenu
->wFlags
|= MF_POPUP
;
4550 menu
->fType
|= MF_POPUP
;
4553 SetLastError( ERROR_INVALID_PARAMETER
);
4558 menu
->fType
&= ~MF_POPUP
;
4561 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4563 if (lpmii
->fType
& MFT_RADIOCHECK
)
4564 menu
->fType
|= MFT_RADIOCHECK
;
4566 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4567 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4569 if (lpmii
->fMask
& MIIM_DATA
)
4570 menu
->dwItemData
= lpmii
->dwItemData
;
4572 if (lpmii
->fMask
& MIIM_BITMAP
)
4573 menu
->hbmpItem
= lpmii
->hbmpItem
;
4575 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4576 menu
->fType
|= MFT_SEPARATOR
;
4578 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4582 /**********************************************************************
4583 * SetMenuItemInfoA (USER32.@)
4585 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4586 const MENUITEMINFOA
*lpmii
)
4589 if( lpmii
->cbSize
!= sizeof( mii
) &&
4590 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4591 SetLastError( ERROR_INVALID_PARAMETER
);
4594 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4595 if( lpmii
->cbSize
!= sizeof( mii
)) {
4596 mii
.cbSize
= sizeof( mii
);
4597 mii
.hbmpItem
= NULL
;
4599 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4600 (const MENUITEMINFOW
*)&mii
, FALSE
);
4603 /**********************************************************************
4604 * SetMenuItemInfoW (USER32.@)
4606 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4607 const MENUITEMINFOW
*lpmii
)
4610 if( lpmii
->cbSize
!= sizeof( mii
) &&
4611 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4612 SetLastError( ERROR_INVALID_PARAMETER
);
4615 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4616 if( lpmii
->cbSize
!= sizeof( mii
)) {
4617 mii
.cbSize
= sizeof( mii
);
4618 mii
.hbmpItem
= NULL
;
4620 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4621 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4624 /**********************************************************************
4625 * SetMenuDefaultItem (USER32.@)
4628 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4634 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4636 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4638 /* reset all default-item flags */
4640 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4642 item
->fState
&= ~MFS_DEFAULT
;
4645 /* no default item */
4654 if ( uItem
>= menu
->nItems
) return FALSE
;
4655 item
[uItem
].fState
|= MFS_DEFAULT
;
4660 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4662 if (item
->wID
== uItem
)
4664 item
->fState
|= MFS_DEFAULT
;
4673 /**********************************************************************
4674 * GetMenuDefaultItem (USER32.@)
4676 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4682 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4684 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4686 /* find default item */
4690 if (! item
) return -1;
4692 while ( !( item
->fState
& MFS_DEFAULT
) )
4695 if (i
>= menu
->nItems
) return -1;
4698 /* default: don't return disabled items */
4699 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4701 /* search rekursiv when needed */
4702 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4705 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4706 if ( -1 != ret
) return ret
;
4708 /* when item not found in submenu, return the popup item */
4710 return ( bypos
) ? i
: item
->wID
;
4715 /**********************************************************************
4716 * InsertMenuItemA (USER32.@)
4718 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4719 const MENUITEMINFOA
*lpmii
)
4721 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4723 if( lpmii
->cbSize
!= sizeof( mii
) &&
4724 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4725 SetLastError( ERROR_INVALID_PARAMETER
);
4728 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4729 if( lpmii
->cbSize
!= sizeof( mii
)) {
4730 mii
.cbSize
= sizeof( mii
);
4731 mii
.hbmpItem
= NULL
;
4733 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4737 /**********************************************************************
4738 * InsertMenuItemW (USER32.@)
4740 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4741 const MENUITEMINFOW
*lpmii
)
4743 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4745 if( lpmii
->cbSize
!= sizeof( mii
) &&
4746 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4747 SetLastError( ERROR_INVALID_PARAMETER
);
4750 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4751 if( lpmii
->cbSize
!= sizeof( mii
)) {
4752 mii
.cbSize
= sizeof( mii
);
4753 mii
.hbmpItem
= NULL
;
4755 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4758 /**********************************************************************
4759 * CheckMenuRadioItem (USER32.@)
4762 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4763 UINT first
, UINT last
, UINT check
,
4766 MENUITEM
*mifirst
, *milast
, *micheck
;
4767 HMENU mfirst
= hMenu
, mlast
= hMenu
, mcheck
= hMenu
;
4769 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu
, first
, last
, check
, bypos
);
4771 mifirst
= MENU_FindItem (&mfirst
, &first
, bypos
);
4772 milast
= MENU_FindItem (&mlast
, &last
, bypos
);
4773 micheck
= MENU_FindItem (&mcheck
, &check
, bypos
);
4775 if (mifirst
== NULL
|| milast
== NULL
|| micheck
== NULL
||
4776 mifirst
> milast
|| mfirst
!= mlast
|| mfirst
!= mcheck
||
4777 micheck
> milast
|| micheck
< mifirst
)
4780 while (mifirst
<= milast
)
4782 if (mifirst
== micheck
)
4784 mifirst
->fType
|= MFT_RADIOCHECK
;
4785 mifirst
->fState
|= MFS_CHECKED
;
4787 mifirst
->fType
&= ~MFT_RADIOCHECK
;
4788 mifirst
->fState
&= ~MFS_CHECKED
;
4797 /**********************************************************************
4798 * GetMenuItemRect (USER32.@)
4800 * ATTENTION: Here, the returned values in rect are the screen
4801 * coordinates of the item just like if the menu was
4802 * always on the upper left side of the application.
4805 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4808 POPUPMENU
*itemMenu
;
4812 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4814 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4815 referenceHwnd
= hwnd
;
4819 itemMenu
= MENU_GetMenu(hMenu
);
4820 if (itemMenu
== NULL
)
4823 if(itemMenu
->hWnd
== 0)
4825 referenceHwnd
= itemMenu
->hWnd
;
4828 if ((rect
== NULL
) || (item
== NULL
))
4833 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4839 /**********************************************************************
4840 * SetMenuInfo (USER32.@)
4843 * MIM_APPLYTOSUBMENUS
4844 * actually use the items to draw the menu
4846 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4850 TRACE("(%p %p)\n", hMenu
, lpmi
);
4852 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4855 if (lpmi
->fMask
& MIM_BACKGROUND
)
4856 menu
->hbrBack
= lpmi
->hbrBack
;
4858 if (lpmi
->fMask
& MIM_HELPID
)
4859 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4861 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4862 menu
->cyMax
= lpmi
->cyMax
;
4864 if (lpmi
->fMask
& MIM_MENUDATA
)
4865 menu
->dwMenuData
= lpmi
->dwMenuData
;
4867 if (lpmi
->fMask
& MIM_STYLE
)
4869 menu
->dwStyle
= lpmi
->dwStyle
;
4870 if (menu
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
4871 if (menu
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
4872 if (menu
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
4873 if (menu
->dwStyle
& MNS_NOTIFYBYPOS
) FIXME("MNS_NOTIFYBYPOS unimplemented\n");
4881 /**********************************************************************
4882 * GetMenuInfo (USER32.@)
4888 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
4891 TRACE("(%p %p)\n", hMenu
, lpmi
);
4893 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
4896 if (lpmi
->fMask
& MIM_BACKGROUND
)
4897 lpmi
->hbrBack
= menu
->hbrBack
;
4899 if (lpmi
->fMask
& MIM_HELPID
)
4900 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
4902 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4903 lpmi
->cyMax
= menu
->cyMax
;
4905 if (lpmi
->fMask
& MIM_MENUDATA
)
4906 lpmi
->dwMenuData
= menu
->dwMenuData
;
4908 if (lpmi
->fMask
& MIM_STYLE
)
4909 lpmi
->dwStyle
= menu
->dwStyle
;
4917 /**********************************************************************
4918 * SetMenuContextHelpId (USER32.@)
4920 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
4924 TRACE("(%p 0x%08lx)\n", hMenu
, dwContextHelpID
);
4926 if ((menu
= MENU_GetMenu(hMenu
)))
4928 menu
->dwContextHelpID
= dwContextHelpID
;
4935 /**********************************************************************
4936 * GetMenuContextHelpId (USER32.@)
4938 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
4942 TRACE("(%p)\n", hMenu
);
4944 if ((menu
= MENU_GetMenu(hMenu
)))
4946 return menu
->dwContextHelpID
;
4951 /**********************************************************************
4952 * MenuItemFromPoint (USER32.@)
4954 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
4956 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
4959 /*FIXME: Do we have to handle hWnd here? */
4960 if (!menu
) return -1;
4961 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
4966 /**********************************************************************
4967 * translate_accelerator
4969 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
4970 BYTE fVirt
, WORD key
, WORD cmd
)
4975 if (wParam
!= key
) return FALSE
;
4977 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
4978 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
4979 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
4981 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
4983 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
4985 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", wParam
& 0xff);
4991 if(fVirt
& FVIRTKEY
)
4993 TRACE_(accel
)("found accel for virt_key %04x (scan %04x)\n",
4994 wParam
, 0xff & HIWORD(lParam
));
4996 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
4997 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5001 if (!(lParam
& 0x01000000)) /* no special_key */
5003 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5004 { /* ^^ ALT pressed */
5005 TRACE_(accel
)("found accel for Alt-%c\n", wParam
& 0xff);
5014 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5018 HMENU hMenu
, hSubMenu
, hSysMenu
;
5019 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5021 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5022 hSysMenu
= get_win_sys_menu( hWnd
);
5024 /* find menu item and ask application to initialize it */
5025 /* 1. in the system menu */
5026 hSubMenu
= hSysMenu
;
5028 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5032 if (!IsWindowEnabled(hWnd
))
5036 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5037 if(hSubMenu
!= hSysMenu
)
5039 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5040 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5041 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5043 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5046 else /* 2. in the window's menu */
5050 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5054 if (!IsWindowEnabled(hWnd
))
5058 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5059 if(hSubMenu
!= hMenu
)
5061 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5062 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5063 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5065 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5072 if (uSysStat
!= (UINT
)-1)
5074 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5081 if (uStat
!= (UINT
)-1)
5087 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5099 if( mesg
==WM_COMMAND
)
5101 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5102 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5104 else if( mesg
==WM_SYSCOMMAND
)
5106 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5107 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5111 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5112 * #0: unknown (please report!)
5113 * #1: for WM_KEYUP,WM_SYSKEYUP
5114 * #2: mouse is captured
5115 * #3: window is disabled
5116 * #4: it's a disabled system menu option
5117 * #5: it's a menu option, but window is iconic
5118 * #6: it's a menu option, but disabled
5120 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5122 ERR_(accel
)(" unknown reason - please report!\n");
5127 /**********************************************************************
5128 * TranslateAcceleratorA (USER32.@)
5129 * TranslateAccelerator (USER32.@)
5131 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5134 LPACCEL16 lpAccelTbl
;
5138 if (!hWnd
|| !msg
) return 0;
5140 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5142 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5146 wParam
= msg
->wParam
;
5148 switch (msg
->message
)
5157 char ch
= LOWORD(wParam
);
5159 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5160 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5168 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5169 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5173 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5174 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5176 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5181 /**********************************************************************
5182 * TranslateAcceleratorW (USER32.@)
5184 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5187 LPACCEL16 lpAccelTbl
;
5190 if (!hWnd
|| !msg
) return 0;
5192 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5194 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5198 switch (msg
->message
)
5210 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08x, lParam %08lx\n",
5211 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5215 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5216 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5218 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);