4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
66 WINE_DECLARE_DEBUG_CHANNEL(accel
);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType
; /* Item type. */
77 UINT fState
; /* Item state. */
78 UINT_PTR wID
; /* Item id. */
79 HMENU hSubMenu
; /* Pop-up menu. */
80 HBITMAP hCheckBit
; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
82 LPWSTR text
; /* Item text. */
83 ULONG_PTR dwItemData
; /* Application defined. */
84 LPWSTR dwTypeData
; /* depends on fMask */
85 HBITMAP hbmpItem
; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect
; /* Item area (relative to menu window) */
88 UINT xTab
; /* X position of text after Tab */
89 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
93 /* Popup menu structure */
95 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic
; /* Magic number */
97 WORD Width
; /* Width of the whole menu */
98 WORD Height
; /* Height of the whole menu */
99 UINT nItems
; /* Number of items in the menu */
100 HWND hWnd
; /* Window containing the menu */
101 MENUITEM
*items
; /* Array of menu items */
102 UINT FocusedItem
; /* Currently focused item */
103 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling
; /* Scroll arrows are active */
106 UINT nScrollPos
; /* Current scroll position */
107 UINT nTotalHeight
; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle
; /* Extended menu style */
110 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack
; /* brush for menu background */
112 DWORD dwContextHelpID
;
113 DWORD dwMenuData
; /* application defined value */
114 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize
; /* Maximum size of the bitmap items */
116 } POPUPMENU
, *LPPOPUPMENU
;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
127 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu
; /* initial menu */
129 HWND hOwnerWnd
; /* where notifications are sent */
133 #define MENU_MAGIC 0x554d /* 'MU' */
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
141 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
142 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
144 /* Space between 2 columns */
145 #define MENU_COL_SPACE 4
147 /* top and bottom margins for popup menus */
148 #define MENU_TOP_MARGIN 3
149 #define MENU_BOTTOM_MARGIN 2
151 /* (other menu->FocusedItem values give the position of the focused item) */
152 #define NO_SELECTED_ITEM 0xffff
154 #define MENU_ITEM_TYPE(flags) \
155 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
157 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
158 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
159 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
161 #define IS_SYSTEM_MENU(menu) \
162 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
164 #define MENUITEMINFO_TYPE_MASK \
165 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
166 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
167 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
168 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
169 #define STATE_MASK (~TYPE_MASK)
170 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
172 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
174 static SIZE menucharsize
;
175 static UINT ODitemheight
; /* default owner drawn item height */
177 /* Use global popup window because there's no way 2 menus can
178 * be tracked at the same time. */
179 static HWND top_popup
;
181 /* Flag set by EndMenu() to force an exit from menu tracking */
182 static BOOL fEndMenu
= FALSE
;
184 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
186 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class
=
193 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
194 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
195 NULL
, /* procA (winproc is Unicode only) */
196 PopupMenuWndProc
, /* procW */
197 sizeof(HMENU
), /* extra */
198 IDC_ARROW
, /* cursor */
199 (HBRUSH
)(COLOR_MENU
+1) /* brush */
203 /***********************************************************************
204 * debug_print_menuitem
206 * Print a menuitem in readable form.
209 #define debug_print_menuitem(pre, mp, post) \
210 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
212 #define MENUOUT(text) \
213 TRACE("%s%s", (count++ ? "," : ""), (text))
215 #define MENUFLAG(bit,text) \
217 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
220 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
223 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
224 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
225 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
226 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
227 TRACE("%s ", prefix
);
229 UINT flags
= mp
->fType
;
230 TRACE( "{ ID=0x%lx", mp
->wID
);
232 TRACE( ", Sub=%p", mp
->hSubMenu
);
236 MENUFLAG( MFT_SEPARATOR
, "sep");
237 MENUFLAG( MFT_OWNERDRAW
, "own");
238 MENUFLAG( MFT_BITMAP
, "bit");
239 MENUFLAG(MF_POPUP
, "pop");
240 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
241 MENUFLAG(MFT_MENUBREAK
, "brk");
242 MENUFLAG(MFT_RADIOCHECK
, "radio");
243 MENUFLAG(MFT_RIGHTORDER
, "rorder");
244 MENUFLAG(MF_SYSMENU
, "sys");
245 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
247 TRACE( "+0x%x", flags
);
253 MENUFLAG(MFS_GRAYED
, "grey");
254 MENUFLAG(MFS_DEFAULT
, "default");
255 MENUFLAG(MFS_DISABLED
, "dis");
256 MENUFLAG(MFS_CHECKED
, "check");
257 MENUFLAG(MFS_HILITE
, "hi");
258 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
259 MENUFLAG(MF_MOUSESELECT
, "mouse");
261 TRACE( "+0x%x", flags
);
264 TRACE( ", Chk=%p", mp
->hCheckBit
);
266 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
268 TRACE( ", Text=%s", debugstr_w(mp
->text
));
270 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
273 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
274 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
276 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
281 TRACE(" %s\n", postfix
);
288 /***********************************************************************
291 * Validate the given menu handle and returns the menu structure pointer.
293 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
295 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
296 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
298 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
304 /***********************************************************************
307 * Get the system menu of a window
309 static HMENU
get_win_sys_menu( HWND hwnd
)
312 WND
*win
= WIN_GetPtr( hwnd
);
313 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
316 WIN_ReleasePtr( win
);
321 /***********************************************************************
324 static HFONT
get_menu_font( BOOL bold
)
326 static HFONT hMenuFont
, hMenuFontBold
;
328 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
332 NONCLIENTMETRICSW ncm
;
335 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
336 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
340 ncm
.lfMenuFont
.lfWeight
+= 300;
341 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
343 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
344 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
348 /* another thread beat us to it */
356 /***********************************************************************
359 static HBITMAP
get_arrow_bitmap(void)
361 static HBITMAP arrow_bitmap
;
363 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
367 /***********************************************************************
368 * get_down_arrow_bitmap
370 static HBITMAP
get_down_arrow_bitmap(void)
372 static HBITMAP arrow_bitmap
;
374 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
378 /***********************************************************************
379 * get_down_arrow_inactive_bitmap
381 static HBITMAP
get_down_arrow_inactive_bitmap(void)
383 static HBITMAP arrow_bitmap
;
385 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
389 /***********************************************************************
390 * get_up_arrow_bitmap
392 static HBITMAP
get_up_arrow_bitmap(void)
394 static HBITMAP arrow_bitmap
;
396 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
400 /***********************************************************************
401 * get_up_arrow_inactive_bitmap
403 static HBITMAP
get_up_arrow_inactive_bitmap(void)
405 static HBITMAP arrow_bitmap
;
407 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
411 /***********************************************************************
414 * Return the default system menu.
416 static HMENU
MENU_CopySysPopup(void)
418 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
419 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
422 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
423 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
424 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
427 ERR("Unable to load default system menu\n" );
429 TRACE("returning %p.\n", hMenu
);
435 /**********************************************************************
438 * Create a copy of the system menu. System menu in Windows is
439 * a special menu bar with the single entry - system menu popup.
440 * This popup is presented to the outside world as a "system menu".
441 * However, the real system menu handle is sometimes seen in the
442 * WM_MENUSELECT parameters (and Word 6 likes it this way).
444 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
448 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
449 if ((hMenu
= CreateMenu()))
451 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
452 menu
->wFlags
= MF_SYSMENU
;
453 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
454 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
457 hPopupMenu
= MENU_CopySysPopup();
461 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
462 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
464 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
465 (UINT_PTR
)hPopupMenu
, NULL
);
467 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
468 menu
->items
[0].fState
= 0;
469 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
471 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
474 DestroyMenu( hMenu
);
476 ERR("failed to load system menu!\n");
481 /***********************************************************************
482 * MENU_InitSysMenuPopup
484 * Grey the appropriate items in System menu.
486 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
490 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
491 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
492 gray
= ((style
& WS_MAXIMIZE
) != 0);
493 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
494 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
495 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
496 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
497 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
498 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
499 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
500 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
502 /* The menu item must keep its state if it's disabled */
504 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
508 /******************************************************************************
510 * UINT MENU_GetStartOfNextColumn(
513 *****************************************************************************/
515 static UINT
MENU_GetStartOfNextColumn(
518 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
522 return NO_SELECTED_ITEM
;
524 i
= menu
->FocusedItem
+ 1;
525 if( i
== NO_SELECTED_ITEM
)
528 for( ; i
< menu
->nItems
; ++i
) {
529 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
533 return NO_SELECTED_ITEM
;
537 /******************************************************************************
539 * UINT MENU_GetStartOfPrevColumn(
542 *****************************************************************************/
544 static UINT
MENU_GetStartOfPrevColumn(
547 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
551 return NO_SELECTED_ITEM
;
553 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
554 return NO_SELECTED_ITEM
;
556 /* Find the start of the column */
558 for(i
= menu
->FocusedItem
; i
!= 0 &&
559 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
563 return NO_SELECTED_ITEM
;
565 for(--i
; i
!= 0; --i
) {
566 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
570 TRACE("ret %d.\n", i
);
577 /***********************************************************************
580 * Find a menu item. Return a pointer on the item, and modifies *hmenu
581 * in case the item was in a sub-menu.
583 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
586 MENUITEM
*fallback
= NULL
;
587 UINT fallback_pos
= 0;
590 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
591 if (wFlags
& MF_BYPOSITION
)
593 if (*nPos
>= menu
->nItems
) return NULL
;
594 return &menu
->items
[*nPos
];
598 MENUITEM
*item
= menu
->items
;
599 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
601 if (item
->fType
& MF_POPUP
)
603 HMENU hsubmenu
= item
->hSubMenu
;
604 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
610 else if (item
->wID
== *nPos
)
612 /* fallback to this item if nothing else found */
617 else if (item
->wID
== *nPos
)
626 *nPos
= fallback_pos
;
631 /***********************************************************************
634 * Find a Sub menu. Return the position of the submenu, and modifies
635 * *hmenu in case it is found in another sub-menu.
636 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
638 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
643 if (((*hmenu
)==(HMENU
)0xffff) ||
644 (!(menu
= MENU_GetMenu(*hmenu
))))
645 return NO_SELECTED_ITEM
;
647 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
648 if(!(item
->fType
& MF_POPUP
)) continue;
649 if (item
->hSubMenu
== hSubTarget
) {
653 HMENU hsubmenu
= item
->hSubMenu
;
654 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
655 if (pos
!= NO_SELECTED_ITEM
) {
661 return NO_SELECTED_ITEM
;
664 /***********************************************************************
667 static void MENU_FreeItemData( MENUITEM
* item
)
670 HeapFree( GetProcessHeap(), 0, item
->text
);
673 /***********************************************************************
674 * MENU_AdjustMenuItemRect
676 * Adjust menu item rectangle according to scrolling state.
679 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
681 if (menu
->bScrolling
)
683 UINT arrow_bitmap_height
;
686 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
687 arrow_bitmap_height
= bmp
.bmHeight
;
688 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
689 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
694 /***********************************************************************
695 * MENU_FindItemByCoords
697 * Find the item at the specified coordinates (screen coords). Does
698 * not work for child windows and therefore should not be called for
699 * an arbitrary system menu.
701 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
702 POINT pt
, UINT
*pos
)
708 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
712 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
715 MENU_AdjustMenuItemRect(menu
, &rect
);
716 if (PtInRect(&rect
, pt
))
726 /***********************************************************************
729 * Find the menu item selected by a key press.
730 * Return item id, -1 if none, -2 if we should close the menu.
732 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
733 WCHAR key
, BOOL forceMenuChar
)
735 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
737 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
741 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
742 MENUITEM
*item
= menu
->items
;
749 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
753 WCHAR
*p
= item
->text
- 2;
756 p
= strchrW (p
+ 2, '&');
758 while (p
!= NULL
&& p
[1] == '&');
759 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
763 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
764 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
765 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
766 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
772 /***********************************************************************
773 * MENU_GetBitmapItemSize
775 * Get the size of a bitmap item.
777 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
781 HBITMAP bmp
= lpitem
->hbmpItem
;
783 size
->cx
= size
->cy
= 0;
785 /* check if there is a magic menu item associated with this item */
786 switch( (INT_PTR
) bmp
)
788 case (INT_PTR
)HBMMENU_CALLBACK
:
790 MEASUREITEMSTRUCT measItem
;
791 measItem
.CtlType
= ODT_MENU
;
793 measItem
.itemID
= lpitem
->wID
;
794 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
795 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
796 measItem
.itemData
= lpitem
->dwItemData
;
797 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
798 size
->cx
= measItem
.itemWidth
;
799 size
->cy
= measItem
.itemHeight
;
803 case (INT_PTR
)HBMMENU_SYSTEM
:
804 if (lpitem
->dwItemData
)
806 bmp
= (HBITMAP
)lpitem
->dwItemData
;
810 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
811 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
812 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
813 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
814 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
815 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
818 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
819 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
820 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
821 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
822 FIXME("Magic %p not implemented\n", bmp
);
825 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
827 size
->cx
= bm
.bmWidth
;
828 size
->cy
= bm
.bmHeight
;
832 /***********************************************************************
833 * MENU_DrawBitmapItem
835 * Draw a bitmap item.
837 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
838 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
844 int w
= rect
->right
- rect
->left
;
845 int h
= rect
->bottom
- rect
->top
;
848 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
851 /* Check if there is a magic menu item associated with this item */
852 if (IS_MAGIC_BITMAP(hbmToDraw
))
857 switch((INT_PTR
)hbmToDraw
)
859 case (INT_PTR
)HBMMENU_SYSTEM
:
860 if (lpitem
->dwItemData
)
862 bmp
= (HBITMAP
)lpitem
->dwItemData
;
863 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
867 static HBITMAP hBmpSysMenu
;
869 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
871 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
872 /* only use right half of the bitmap */
873 bmp_xoffset
= bm
.bmWidth
/ 2;
874 bm
.bmWidth
-= bmp_xoffset
;
877 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
878 flags
= DFCS_CAPTIONRESTORE
;
880 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
881 flags
= DFCS_CAPTIONMIN
;
883 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
884 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
886 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
887 flags
= DFCS_CAPTIONCLOSE
;
889 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
890 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
892 case (INT_PTR
)HBMMENU_CALLBACK
:
894 DRAWITEMSTRUCT drawItem
;
895 drawItem
.CtlType
= ODT_MENU
;
897 drawItem
.itemID
= lpitem
->wID
;
898 drawItem
.itemAction
= odaction
;
899 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
900 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
901 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
902 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
903 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
904 drawItem
.hwndItem
= (HWND
)hmenu
;
906 drawItem
.itemData
= lpitem
->dwItemData
;
907 drawItem
.rcItem
= *rect
;
908 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
912 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
913 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
914 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
915 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
917 FIXME("Magic %p not implemented\n", hbmToDraw
);
921 InflateRect( &r
, -1, -1 );
922 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
923 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
927 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
930 hdcMem
= CreateCompatibleDC( hdc
);
931 SelectObject( hdcMem
, bmp
);
933 /* handle fontsize > bitmap_height */
934 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
936 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
937 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
938 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
939 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
944 /***********************************************************************
947 * Calculate the size of the menu item and store it in lpitem->rect.
949 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
950 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
953 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
954 UINT arrow_bitmap_width
;
958 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
959 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
960 (menuBar
? " (MenuBar)" : ""));
962 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
963 arrow_bitmap_width
= bm
.bmWidth
;
965 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
966 if( !menucharsize
.cx
) {
967 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
968 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
969 * but it is unlikely an application will depend on that */
970 ODitemheight
= HIWORD( GetDialogBaseUnits());
973 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
975 if (lpitem
->fType
& MF_OWNERDRAW
)
977 MEASUREITEMSTRUCT mis
;
978 mis
.CtlType
= ODT_MENU
;
980 mis
.itemID
= lpitem
->wID
;
981 mis
.itemData
= lpitem
->dwItemData
;
982 mis
.itemHeight
= ODitemheight
;
984 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
985 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
986 * width of a menufont character to the width of an owner-drawn menu.
988 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
990 /* under at least win95 you seem to be given a standard
991 height for the menu and the height value is ignored */
992 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
994 lpitem
->rect
.bottom
+= mis
.itemHeight
;
996 TRACE("id=%04lx size=%dx%d\n",
997 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
998 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1002 if (lpitem
->fType
& MF_SEPARATOR
)
1004 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1006 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1014 if (lpitem
->hbmpItem
) {
1017 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1018 /* Keep the size of the bitmap in callback mode to be able
1019 * to draw it correctly */
1020 lpitem
->bmpsize
= size
;
1021 lppop
->maxBmpSize
.cx
= max( lppop
->maxBmpSize
.cx
, size
.cx
);
1022 lppop
->maxBmpSize
.cy
= max( lppop
->maxBmpSize
.cy
, size
.cy
);
1023 lpitem
->rect
.right
+= size
.cx
+ 2;
1024 itemheight
= size
.cy
+ 2;
1026 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1027 lpitem
->rect
.right
+= check_bitmap_width
;
1028 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1029 lpitem
->xTab
= lpitem
->rect
.right
;
1030 lpitem
->rect
.right
+= arrow_bitmap_width
;
1031 } else if (lpitem
->hbmpItem
) { /* menuBar */
1034 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1035 lpitem
->bmpsize
= size
;
1036 lpitem
->rect
.right
+= size
.cx
;
1037 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1038 itemheight
= size
.cy
;
1041 /* it must be a text item - unless it's the system menu */
1042 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1043 HFONT hfontOld
= NULL
;
1044 RECT rc
= lpitem
->rect
;
1045 LONG txtheight
, txtwidth
;
1047 if ( lpitem
->fState
& MFS_DEFAULT
) {
1048 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1051 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1052 DT_SINGLELINE
|DT_CALCRECT
);
1053 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1054 itemheight
= max( max( itemheight
, txtheight
),
1055 GetSystemMetrics( SM_CYMENU
) - 1);
1056 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1058 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1061 int n
= (int)( p
- lpitem
->text
);
1062 /* Item contains a tab (only meaningful in popup menus) */
1063 /* get text size before the tab */
1064 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1065 DT_SINGLELINE
|DT_CALCRECT
);
1066 txtwidth
= rc
.right
- rc
.left
;
1067 p
+= 1; /* advance past the Tab */
1068 /* get text size after the tab */
1069 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1070 DT_SINGLELINE
|DT_CALCRECT
);
1071 lpitem
->xTab
+= txtwidth
;
1072 txtheight
= max( txtheight
, tmpheight
);
1073 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1074 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1076 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1077 DT_SINGLELINE
|DT_CALCRECT
);
1078 txtwidth
= rc
.right
- rc
.left
;
1079 lpitem
->xTab
+= txtwidth
;
1081 lpitem
->rect
.right
+= 2 + txtwidth
;
1082 itemheight
= max( itemheight
,
1083 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1085 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1086 } else if( menuBar
) {
1087 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1089 lpitem
->rect
.bottom
+= itemheight
;
1090 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1094 /***********************************************************************
1095 * MENU_GetMaxPopupHeight
1098 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1101 return lppop
->cyMax
;
1102 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1106 /***********************************************************************
1107 * MENU_PopupMenuCalcSize
1109 * Calculate the size of a popup menu.
1111 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1116 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1118 lppop
->Width
= lppop
->Height
= 0;
1119 if (lppop
->nItems
== 0) return;
1122 SelectObject( hdc
, get_menu_font(FALSE
));
1127 lppop
->maxBmpSize
.cx
= 0;
1128 lppop
->maxBmpSize
.cy
= 0;
1130 while (start
< lppop
->nItems
)
1132 lpitem
= &lppop
->items
[start
];
1134 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1135 orgX
+= MENU_COL_SPACE
;
1136 orgY
= MENU_TOP_MARGIN
;
1138 maxTab
= maxTabWidth
= 0;
1139 /* Parse items until column break or end of menu */
1140 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1143 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1145 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1146 maxX
= max( maxX
, lpitem
->rect
.right
);
1147 orgY
= lpitem
->rect
.bottom
;
1148 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1150 maxTab
= max( maxTab
, lpitem
->xTab
);
1151 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1155 /* Finish the column (set all items to the largest width found) */
1156 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1157 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1159 lpitem
->rect
.right
= maxX
;
1160 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1161 lpitem
->xTab
= maxTab
;
1164 lppop
->Height
= max( lppop
->Height
, orgY
);
1167 lppop
->Width
= maxX
;
1169 /* space for 3d border */
1170 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1173 /* Adjust popup height if it exceeds maximum */
1174 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1175 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1176 if (lppop
->Height
>= maxHeight
)
1178 lppop
->Height
= maxHeight
;
1179 lppop
->bScrolling
= TRUE
;
1183 lppop
->bScrolling
= FALSE
;
1186 ReleaseDC( 0, hdc
);
1190 /***********************************************************************
1191 * MENU_MenuBarCalcSize
1193 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1194 * height is off by 1 pixel which causes lengthy window relocations when
1195 * active document window is maximized/restored.
1197 * Calculate the size of the menu bar.
1199 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1200 LPPOPUPMENU lppop
, HWND hwndOwner
)
1203 UINT start
, i
, helpPos
;
1204 int orgX
, orgY
, maxY
;
1206 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1207 if (lppop
->nItems
== 0) return;
1208 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1209 lppop
->Width
= lprect
->right
- lprect
->left
;
1211 maxY
= lprect
->top
+1;
1214 lppop
->maxBmpSize
.cx
= 0;
1215 lppop
->maxBmpSize
.cy
= 0;
1216 while (start
< lppop
->nItems
)
1218 lpitem
= &lppop
->items
[start
];
1219 orgX
= lprect
->left
;
1222 /* Parse items until line break or end of menu */
1223 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1225 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1227 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1229 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1230 debug_print_menuitem (" item: ", lpitem
, "");
1231 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1233 if (lpitem
->rect
.right
> lprect
->right
)
1235 if (i
!= start
) break;
1236 else lpitem
->rect
.right
= lprect
->right
;
1238 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1239 orgX
= lpitem
->rect
.right
;
1242 /* Finish the line (set all items to the largest height found) */
1243 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1246 lprect
->bottom
= maxY
;
1247 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1249 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1250 /* the last item (if several lines, only move the last line) */
1251 if (helpPos
== ~0U) return;
1252 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1253 orgY
= lpitem
->rect
.top
;
1254 orgX
= lprect
->right
;
1255 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1256 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1257 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1258 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1259 lpitem
->rect
.right
= orgX
;
1260 orgX
= lpitem
->rect
.left
;
1265 /***********************************************************************
1266 * MENU_DrawScrollArrows
1268 * Draw scroll arrows.
1271 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1273 HDC hdcMem
= CreateCompatibleDC(hdc
);
1274 HBITMAP hOrigBitmap
;
1275 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1279 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1280 arrow_bitmap_width
= bmp
.bmWidth
;
1281 arrow_bitmap_height
= bmp
.bmHeight
;
1284 if (lppop
->nScrollPos
)
1285 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1287 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1290 rect
.right
= lppop
->Width
;
1291 rect
.bottom
= arrow_bitmap_height
;
1292 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1293 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1294 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1295 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1296 rect
.bottom
= lppop
->Height
;
1297 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1298 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1299 SelectObject(hdcMem
, get_down_arrow_bitmap());
1301 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1302 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1303 lppop
->Height
- arrow_bitmap_height
,
1304 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1305 SelectObject(hdcMem
, hOrigBitmap
);
1310 /***********************************************************************
1313 * Draws the popup-menu arrow.
1315 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1316 UINT arrow_bitmap_height
)
1318 HDC hdcMem
= CreateCompatibleDC( hdc
);
1319 HBITMAP hOrigBitmap
;
1321 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1322 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1323 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1324 arrow_bitmap_width
, arrow_bitmap_height
,
1325 hdcMem
, 0, 0, SRCCOPY
);
1326 SelectObject( hdcMem
, hOrigBitmap
);
1329 /***********************************************************************
1332 * Draw a single menu item.
1334 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1335 UINT height
, BOOL menuBar
, UINT odaction
)
1338 BOOL flat_menu
= FALSE
;
1340 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1341 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1344 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1348 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1349 arrow_bitmap_width
= bmp
.bmWidth
;
1350 arrow_bitmap_height
= bmp
.bmHeight
;
1353 if (lpitem
->fType
& MF_SYSMENU
)
1355 if( !IsIconic(hwnd
) )
1356 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1360 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1361 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1365 if (lpitem
->fState
& MF_HILITE
)
1367 if(menuBar
&& !flat_menu
) {
1368 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1369 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1371 if(lpitem
->fState
& MF_GRAYED
)
1372 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1374 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1375 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1380 if (lpitem
->fState
& MF_GRAYED
)
1381 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1383 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1384 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1387 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1388 rect
= lpitem
->rect
;
1389 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1391 if (lpitem
->fType
& MF_OWNERDRAW
)
1394 ** Experimentation under Windows reveals that an owner-drawn
1395 ** menu is given the rectangle which includes the space it requested
1396 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1397 ** and a popup-menu arrow. This is the value of lpitem->rect.
1398 ** Windows will leave all drawing to the application except for
1399 ** the popup-menu arrow. Windows always draws that itself, after
1400 ** the menu owner has finished drawing.
1404 dis
.CtlType
= ODT_MENU
;
1406 dis
.itemID
= lpitem
->wID
;
1407 dis
.itemData
= lpitem
->dwItemData
;
1409 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1410 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1411 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1412 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1413 dis
.hwndItem
= (HWND
)hmenu
;
1416 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1417 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1418 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1419 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1420 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1421 /* Draw the popup-menu arrow */
1422 if (lpitem
->fType
& MF_POPUP
)
1423 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1424 arrow_bitmap_height
);
1428 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1430 if (lpitem
->fState
& MF_HILITE
)
1434 InflateRect (&rect
, -1, -1);
1435 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1436 InflateRect (&rect
, 1, 1);
1437 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1442 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1444 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1448 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1450 SetBkMode( hdc
, TRANSPARENT
);
1452 /* vertical separator */
1453 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1458 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1460 rc
.bottom
= height
- 3;
1463 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1464 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1465 LineTo( hdc
, rc
.left
, rc
.bottom
);
1466 SelectObject( hdc
, oldPen
);
1469 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1472 /* horizontal separator */
1473 if (lpitem
->fType
& MF_SEPARATOR
)
1480 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1483 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1484 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1485 LineTo( hdc
, rc
.right
, rc
.top
);
1486 SelectObject( hdc
, oldPen
);
1489 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1493 /* helper lines for debugging */
1494 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1495 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1496 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1497 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1500 if (lpitem
->hbmpItem
) {
1501 /* calculate the bitmap rectangle in coordinates relative
1502 * to the item rectangle */
1504 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1507 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1509 else if (menu
->dwStyle
& MNS_NOCHECK
)
1511 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1514 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1515 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1516 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1519 bmprc
.top
= (rect
.bottom
- rect
.top
-
1520 lpitem
->bmpsize
.cy
) / 2;
1521 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1527 INT y
= rect
.top
+ rect
.bottom
;
1529 int checked
= FALSE
;
1530 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1531 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1532 /* Draw the check mark
1535 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1537 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1538 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1539 lpitem
->hUnCheckBit
;
1540 if (bm
) /* we have a custom bitmap */
1542 HDC hdcMem
= CreateCompatibleDC( hdc
);
1544 SelectObject( hdcMem
, bm
);
1545 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1546 check_bitmap_width
, check_bitmap_height
,
1547 hdcMem
, 0, 0, SRCCOPY
);
1551 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1554 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1555 check_bitmap_height
, 1, 1, NULL
);
1556 HDC hdcMem
= CreateCompatibleDC( hdc
);
1558 SelectObject( hdcMem
, bm
);
1559 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1560 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1561 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1562 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1563 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1564 hdcMem
, 0, 0, SRCCOPY
);
1570 if( lpitem
->hbmpItem
&&
1571 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1573 /* some applications make this assumption on the DC's origin */
1574 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1575 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1577 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1579 /* Draw the popup-menu arrow */
1580 if (lpitem
->fType
& MF_POPUP
)
1581 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1582 arrow_bitmap_height
);
1584 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1585 rect
.left
+= check_bitmap_width
;
1586 rect
.right
-= arrow_bitmap_width
;
1588 else if( lpitem
->hbmpItem
)
1589 { /* Draw the bitmap */
1592 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1593 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1595 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1597 /* process text if present */
1603 UINT uFormat
= (menuBar
) ?
1604 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1605 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1607 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1608 rect
.left
+= menu
->maxBmpSize
.cx
;
1610 if ( lpitem
->fState
& MFS_DEFAULT
)
1612 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1616 if( lpitem
->hbmpItem
)
1617 rect
.left
+= lpitem
->bmpsize
.cx
;
1618 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1619 rect
.left
+= menucharsize
.cx
;
1620 rect
.right
-= menucharsize
.cx
;
1623 for (i
= 0; lpitem
->text
[i
]; i
++)
1624 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1627 if(lpitem
->fState
& MF_GRAYED
)
1629 if (!(lpitem
->fState
& MF_HILITE
) )
1631 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1632 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1633 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1634 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1636 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1639 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1641 /* paint the shortcut text */
1642 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1644 if (lpitem
->text
[i
] == '\t')
1646 rect
.left
= lpitem
->xTab
;
1647 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1651 rect
.right
= lpitem
->xTab
;
1652 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1655 if(lpitem
->fState
& MF_GRAYED
)
1657 if (!(lpitem
->fState
& MF_HILITE
) )
1659 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1660 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1661 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1662 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1664 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1666 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1670 SelectObject (hdc
, hfontOld
);
1675 /***********************************************************************
1676 * MENU_DrawPopupMenu
1678 * Paint a popup menu.
1680 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1682 HBRUSH hPrevBrush
= 0;
1685 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1687 GetClientRect( hwnd
, &rect
);
1689 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1690 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1694 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1696 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1700 BOOL flat_menu
= FALSE
;
1702 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1704 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1706 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1708 if( (menu
= MENU_GetMenu( hmenu
)))
1710 /* draw menu items */
1717 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1718 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1719 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1721 /* draw scroll arrows */
1722 if (menu
->bScrolling
)
1723 MENU_DrawScrollArrows(menu
, hdc
);
1727 SelectObject( hdc
, hPrevBrush
);
1732 /***********************************************************************
1735 * Paint a menu bar. Returns the height of the menu bar.
1736 * called from [windows/nonclient.c]
1738 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1743 HMENU hMenu
= GetMenu(hwnd
);
1745 lppop
= MENU_GetMenu( hMenu
);
1746 if (lppop
== NULL
|| lprect
== NULL
)
1748 return GetSystemMetrics(SM_CYMENU
);
1753 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1755 if (lppop
->Height
== 0)
1756 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1758 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1760 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1761 return lppop
->Height
;
1764 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1768 /***********************************************************************
1771 * Display a popup menu.
1773 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1774 INT x
, INT y
, INT xanchor
, INT yanchor
)
1782 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1783 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1785 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1786 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1788 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1789 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1792 /* store the owner for DrawItem */
1793 menu
->hwndOwner
= hwndOwner
;
1795 menu
->nScrollPos
= 0;
1796 MENU_PopupMenuCalcSize( menu
);
1798 /* adjust popup menu pos so that it fits within the desktop */
1800 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1801 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1803 /* FIXME: should use item rect */
1806 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1807 info
.cbSize
= sizeof(info
);
1808 GetMonitorInfoW( monitor
, &info
);
1810 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1811 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1813 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1814 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1816 if( x
+ width
> info
.rcWork
.right
)
1818 if( xanchor
&& x
>= width
- xanchor
)
1819 x
-= width
- xanchor
;
1821 if( x
+ width
> info
.rcWork
.right
)
1822 x
= info
.rcWork
.right
- width
;
1824 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1826 if( y
+ height
> info
.rcWork
.bottom
)
1828 if( yanchor
&& y
>= height
+ yanchor
)
1829 y
-= height
+ yanchor
;
1831 if( y
+ height
> info
.rcWork
.bottom
)
1832 y
= info
.rcWork
.bottom
- height
;
1834 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1836 /* NOTE: In Windows, top menu popup is not owned. */
1837 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1838 WS_POPUP
, x
, y
, width
, height
,
1839 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1841 if( !menu
->hWnd
) return FALSE
;
1842 if (!top_popup
) top_popup
= menu
->hWnd
;
1844 /* Display the window */
1846 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1847 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1848 UpdateWindow( menu
->hWnd
);
1853 /***********************************************************************
1854 * MENU_EnsureMenuItemVisible
1857 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1859 if (lppop
->bScrolling
)
1861 MENUITEM
*item
= &lppop
->items
[wIndex
];
1862 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1863 UINT nOldPos
= lppop
->nScrollPos
;
1865 UINT arrow_bitmap_height
;
1868 GetClientRect(lppop
->hWnd
, &rc
);
1870 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1871 arrow_bitmap_height
= bmp
.bmHeight
;
1873 rc
.top
+= arrow_bitmap_height
;
1874 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1876 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1877 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1880 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1881 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1882 MENU_DrawScrollArrows(lppop
, hdc
);
1884 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1886 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1887 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1888 MENU_DrawScrollArrows(lppop
, hdc
);
1894 /***********************************************************************
1897 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1898 BOOL sendMenuSelect
, HMENU topmenu
)
1903 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1905 lppop
= MENU_GetMenu( hmenu
);
1906 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1908 if (lppop
->FocusedItem
== wIndex
) return;
1909 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1910 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1911 if (!top_popup
) top_popup
= lppop
->hWnd
;
1913 SelectObject( hdc
, get_menu_font(FALSE
));
1915 /* Clear previous highlighted item */
1916 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1918 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1919 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1920 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1924 /* Highlight new item (if any) */
1925 lppop
->FocusedItem
= wIndex
;
1926 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1928 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1929 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1930 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1931 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1932 &lppop
->items
[wIndex
], lppop
->Height
,
1933 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1937 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1938 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1939 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1940 ip
->fType
| ip
->fState
|
1941 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1944 else if (sendMenuSelect
) {
1947 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1948 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1949 MENUITEM
*ip
= &ptm
->items
[pos
];
1950 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1951 ip
->fType
| ip
->fState
|
1952 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1956 ReleaseDC( lppop
->hWnd
, hdc
);
1960 /***********************************************************************
1961 * MENU_MoveSelection
1963 * Moves currently selected item according to the offset parameter.
1964 * If there is no selection then it should select the last item if
1965 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1967 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1972 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1974 menu
= MENU_GetMenu( hmenu
);
1975 if ((!menu
) || (!menu
->items
)) return;
1977 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1979 if( menu
->nItems
== 1 ) return; else
1980 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1982 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1984 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1989 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1990 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1991 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1993 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1999 /**********************************************************************
2002 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2005 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
2008 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
2009 TRACE("flags=%x str=%p\n", flags
, str
);
2011 if (IS_STRING_ITEM(flags
))
2013 LPWSTR prevText
= item
->text
;
2016 flags
|= MF_SEPARATOR
;
2022 /* Item beginning with a backspace is a help item */
2028 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
2030 strcpyW( text
, str
);
2033 item
->hbmpItem
= NULL
;
2034 HeapFree( GetProcessHeap(), 0, prevText
);
2036 else if(( flags
& MFT_BITMAP
)) {
2037 item
->hbmpItem
= HBITMAP_32(LOWORD(str
));
2038 /* setting bitmap clears text */
2039 HeapFree( GetProcessHeap(), 0, item
->text
);
2043 if (flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
2045 if (flags
& MF_OWNERDRAW
)
2046 item
->dwItemData
= (DWORD_PTR
)str
;
2048 item
->dwItemData
= 0;
2050 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
2051 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
2053 if (flags
& MF_POPUP
)
2055 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
2056 if (menu
) menu
->wFlags
|= MF_POPUP
;
2068 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
2070 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
2071 flags
|= MF_POPUP
; /* keep popup */
2073 item
->fType
= flags
& TYPE_MASK
;
2074 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2075 for ModifyMenu, but Windows accepts it */
2076 item
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
2078 /* Don't call SetRectEmpty here! */
2080 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
2085 /**********************************************************************
2088 * Insert (allocate) a new item into a menu.
2090 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2095 if (!(menu
= MENU_GetMenu(hMenu
)))
2098 /* Find where to insert new item */
2100 if (flags
& MF_BYPOSITION
) {
2101 if (pos
> menu
->nItems
)
2104 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2107 if (!(menu
= MENU_GetMenu( hMenu
)))
2112 /* Make sure that MDI system buttons stay on the right side.
2113 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2114 * regardless of their id.
2116 while (pos
> 0 && (menu
->items
[pos
- 1].fType
& MFT_BITMAP
) &&
2117 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2118 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2121 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2123 /* Create new items array */
2125 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2128 WARN("allocation failed\n" );
2131 if (menu
->nItems
> 0)
2133 /* Copy the old array into the new one */
2134 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2135 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2136 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2137 HeapFree( GetProcessHeap(), 0, menu
->items
);
2139 menu
->items
= newItems
;
2141 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2142 menu
->Height
= 0; /* force size recalculate */
2143 return &newItems
[pos
];
2147 /**********************************************************************
2148 * MENU_ParseResource
2150 * Parse a standard menu resource and add items to the menu.
2151 * Return a pointer to the end of the resource.
2153 * NOTE: flags is equivalent to the mtOption field
2155 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2163 flags
= GET_WORD(res
);
2164 end_flag
= flags
& MF_END
;
2165 /* Remove MF_END because it has the same value as MF_HILITE */
2167 res
+= sizeof(WORD
);
2168 if (!(flags
& MF_POPUP
))
2171 res
+= sizeof(WORD
);
2174 if (!unicode
) res
+= strlen(str
) + 1;
2175 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2176 if (flags
& MF_POPUP
)
2178 HMENU hSubMenu
= CreatePopupMenu();
2179 if (!hSubMenu
) return NULL
;
2180 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2182 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2183 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2185 else /* Not a popup */
2187 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2188 else AppendMenuW( hMenu
, flags
, id
,
2189 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2191 } while (!end_flag
);
2196 /**********************************************************************
2197 * MENUEX_ParseResource
2199 * Parse an extended menu resource and add items to the menu.
2200 * Return a pointer to the end of the resource.
2202 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2208 mii
.cbSize
= sizeof(mii
);
2209 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2210 mii
.fType
= GET_DWORD(res
);
2211 res
+= sizeof(DWORD
);
2212 mii
.fState
= GET_DWORD(res
);
2213 res
+= sizeof(DWORD
);
2214 mii
.wID
= GET_DWORD(res
);
2215 res
+= sizeof(DWORD
);
2216 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2217 res
+= sizeof(WORD
);
2218 /* Align the text on a word boundary. */
2219 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2220 mii
.dwTypeData
= (LPWSTR
) res
;
2221 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2222 /* Align the following fields on a dword boundary. */
2223 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2225 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2226 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2228 if (resinfo
& 1) { /* Pop-up? */
2229 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2230 res
+= sizeof(DWORD
);
2231 mii
.hSubMenu
= CreatePopupMenu();
2234 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2235 DestroyMenu(mii
.hSubMenu
);
2238 mii
.fMask
|= MIIM_SUBMENU
;
2239 mii
.fType
|= MF_POPUP
;
2241 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2243 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2244 mii
.wID
, mii
.fType
);
2245 mii
.fType
|= MF_SEPARATOR
;
2247 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2248 } while (!(resinfo
& MF_END
));
2253 /***********************************************************************
2256 * Return the handle of the selected sub-popup menu (if any).
2258 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2263 menu
= MENU_GetMenu( hmenu
);
2265 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2267 item
= &menu
->items
[menu
->FocusedItem
];
2268 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2269 return item
->hSubMenu
;
2274 /***********************************************************************
2275 * MENU_HideSubPopups
2277 * Hide the sub-popup menus of this menu.
2279 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2280 BOOL sendMenuSelect
, UINT wFlags
)
2282 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2284 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2286 if (menu
&& top_popup
)
2292 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2294 item
= &menu
->items
[menu
->FocusedItem
];
2295 if (!(item
->fType
& MF_POPUP
) ||
2296 !(item
->fState
& MF_MOUSESELECT
)) return;
2297 item
->fState
&= ~MF_MOUSESELECT
;
2298 hsubmenu
= item
->hSubMenu
;
2301 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2302 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2303 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2304 DestroyWindow( submenu
->hWnd
);
2307 if (!(wFlags
& TPM_NONOTIFY
))
2308 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2309 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2314 /***********************************************************************
2317 * Display the sub-menu of the selected item of this menu.
2318 * Return the handle of the submenu, or hmenu if no submenu to display.
2320 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2321 BOOL selectFirst
, UINT wFlags
)
2328 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2330 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2332 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2334 item
= &menu
->items
[menu
->FocusedItem
];
2335 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2338 /* message must be sent before using item,
2339 because nearly everything may be changed by the application ! */
2341 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2342 if (!(wFlags
& TPM_NONOTIFY
))
2343 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2344 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2346 item
= &menu
->items
[menu
->FocusedItem
];
2349 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2350 if (!(item
->fState
& MF_HILITE
))
2352 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2353 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2355 SelectObject( hdc
, get_menu_font(FALSE
));
2357 item
->fState
|= MF_HILITE
;
2358 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2359 ReleaseDC( menu
->hWnd
, hdc
);
2361 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2364 item
->fState
|= MF_MOUSESELECT
;
2366 if (IS_SYSTEM_MENU(menu
))
2368 MENU_InitSysMenuPopup(item
->hSubMenu
,
2369 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2370 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2372 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2373 rect
.top
= rect
.bottom
;
2374 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2375 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2379 GetWindowRect( menu
->hWnd
, &rect
);
2380 if (menu
->wFlags
& MF_POPUP
)
2382 RECT rc
= item
->rect
;
2384 MENU_AdjustMenuItemRect(menu
, &rc
);
2386 /* The first item in the popup menu has to be at the
2387 same y position as the focused menu item */
2388 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2389 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2390 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2391 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2392 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2396 rect
.left
+= item
->rect
.left
;
2397 rect
.top
+= item
->rect
.bottom
;
2398 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2399 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2403 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2404 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2406 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2407 return item
->hSubMenu
;
2412 /**********************************************************************
2415 HWND
MENU_IsMenuActive(void)
2420 /***********************************************************************
2423 * Walks menu chain trying to find a menu pt maps to.
2425 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2427 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2428 UINT item
= menu
->FocusedItem
;
2431 /* try subpopup first (if any) */
2432 ret
= (item
!= NO_SELECTED_ITEM
&&
2433 (menu
->items
[item
].fType
& MF_POPUP
) &&
2434 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2435 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2437 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2439 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2440 if( menu
->wFlags
& MF_POPUP
)
2442 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2444 else if (ht
== HTSYSMENU
)
2445 ret
= get_win_sys_menu( menu
->hWnd
);
2446 else if (ht
== HTMENU
)
2447 ret
= GetMenu( menu
->hWnd
);
2452 /***********************************************************************
2453 * MENU_ExecFocusedItem
2455 * Execute a menu item (for instance when user pressed Enter).
2456 * Return the wID of the executed item. Otherwise, -1 indicating
2457 * that no menu item was executed, -2 if a popup is shown;
2458 * Have to receive the flags for the TrackPopupMenu options to avoid
2459 * sending unwanted message.
2462 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2465 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2467 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2469 if (!menu
|| !menu
->nItems
||
2470 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2472 item
= &menu
->items
[menu
->FocusedItem
];
2474 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2476 if (!(item
->fType
& MF_POPUP
))
2478 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2480 /* If TPM_RETURNCMD is set you return the id, but
2481 do not send a message to the owner */
2482 if(!(wFlags
& TPM_RETURNCMD
))
2484 if( menu
->wFlags
& MF_SYSMENU
)
2485 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2486 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2489 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2490 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2492 if (dwStyle
& MNS_NOTIFYBYPOS
)
2493 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2496 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2504 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2511 /***********************************************************************
2512 * MENU_SwitchTracking
2514 * Helper function for menu navigation routines.
2516 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2518 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2519 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2521 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2523 if( pmt
->hTopMenu
!= hPtMenu
&&
2524 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2526 /* both are top level menus (system and menu-bar) */
2527 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2528 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2529 pmt
->hTopMenu
= hPtMenu
;
2531 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2532 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2536 /***********************************************************************
2539 * Return TRUE if we can go on with menu tracking.
2541 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2543 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2548 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2551 if( IS_SYSTEM_MENU(ptmenu
) )
2552 item
= ptmenu
->items
;
2554 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2558 if( ptmenu
->FocusedItem
!= id
)
2559 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2561 /* If the popup menu is not already "popped" */
2562 if(!(item
->fState
& MF_MOUSESELECT
))
2564 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2569 /* Else the click was on the menu bar, finish the tracking */
2574 /***********************************************************************
2577 * Return the value of MENU_ExecFocusedItem if
2578 * the selected item was not a popup. Else open the popup.
2579 * A -1 return value indicates that we go on with menu tracking.
2582 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2584 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2589 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2592 if( IS_SYSTEM_MENU(ptmenu
) )
2593 item
= ptmenu
->items
;
2595 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2597 if( item
&& (ptmenu
->FocusedItem
== id
))
2599 debug_print_menuitem ("FocusedItem: ", item
, "");
2601 if( !(item
->fType
& MF_POPUP
) )
2603 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2604 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2605 return executedMenuId
;
2608 /* If we are dealing with the top-level menu */
2609 /* and this is a click on an already "popped" item: */
2610 /* Stop the menu tracking and close the opened submenus */
2611 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2614 ptmenu
->bTimeToHide
= TRUE
;
2620 /***********************************************************************
2623 * Return TRUE if we can go on with menu tracking.
2625 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2627 UINT id
= NO_SELECTED_ITEM
;
2628 POPUPMENU
*ptmenu
= NULL
;
2632 ptmenu
= MENU_GetMenu( hPtMenu
);
2633 if( IS_SYSTEM_MENU(ptmenu
) )
2636 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2639 if( id
== NO_SELECTED_ITEM
)
2641 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2642 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2645 else if( ptmenu
->FocusedItem
!= id
)
2647 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2648 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2654 /***********************************************************************
2657 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2659 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2661 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2664 /* When skipping left, we need to do something special after the
2666 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2670 /* When skipping right, for the non-system menu, we need to
2671 handle the last non-special menu item (ie skip any window
2672 icons such as MDI maximize, restore or close) */
2673 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2675 UINT i
= menu
->FocusedItem
+ 1;
2676 while (i
< menu
->nItems
) {
2677 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2678 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2682 if (i
== menu
->nItems
) {
2686 /* When skipping right, we need to cater for the system menu */
2687 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2689 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2696 MDINEXTMENU next_menu
;
2701 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2702 next_menu
.hmenuNext
= 0;
2703 next_menu
.hwndNext
= 0;
2704 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2706 TRACE("%p [%p] -> %p [%p]\n",
2707 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2709 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2711 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2712 hNewWnd
= pmt
->hOwnerWnd
;
2713 if( IS_SYSTEM_MENU(menu
) )
2715 /* switch to the menu bar */
2717 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2721 menu
= MENU_GetMenu( hNewMenu
);
2722 id
= menu
->nItems
- 1;
2724 /* Skip backwards over any system predefined icons,
2725 eg. MDI close, restore etc icons */
2727 (menu
->items
[id
].wID
>= SC_SIZE
&&
2728 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2731 else if (style
& WS_SYSMENU
)
2733 /* switch to the system menu */
2734 hNewMenu
= get_win_sys_menu( hNewWnd
);
2738 else /* application returned a new menu to switch to */
2740 hNewMenu
= next_menu
.hmenuNext
;
2741 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2743 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2745 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2747 if (style
& WS_SYSMENU
&&
2748 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2750 /* get the real system menu */
2751 hNewMenu
= get_win_sys_menu(hNewWnd
);
2753 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2755 /* FIXME: Not sure what to do here;
2756 * perhaps try to track hNewMenu as a popup? */
2758 TRACE(" -- got confused.\n");
2765 if( hNewMenu
!= pmt
->hTopMenu
)
2767 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2769 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2770 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2773 if( hNewWnd
!= pmt
->hOwnerWnd
)
2775 pmt
->hOwnerWnd
= hNewWnd
;
2776 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2779 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2780 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2787 /***********************************************************************
2790 * The idea is not to show the popup if the next input message is
2791 * going to hide it anyway.
2793 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2797 msg
.hwnd
= pmt
->hOwnerWnd
;
2799 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2800 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2805 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2806 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2808 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2809 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2810 if( msg
.message
== WM_KEYDOWN
&&
2811 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2813 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2820 /* failures go through this */
2821 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2825 /***********************************************************************
2828 * Handle a VK_ESCAPE key event in a menu.
2830 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2832 BOOL bEndMenu
= TRUE
;
2834 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2836 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2838 if (menu
->wFlags
& MF_POPUP
)
2840 HMENU hmenutmp
, hmenuprev
;
2842 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2844 /* close topmost popup */
2845 while (hmenutmp
!= pmt
->hCurrentMenu
)
2847 hmenuprev
= hmenutmp
;
2848 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2851 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2852 pmt
->hCurrentMenu
= hmenuprev
;
2860 /***********************************************************************
2863 * Handle a VK_LEFT key event in a menu.
2865 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2868 HMENU hmenutmp
, hmenuprev
;
2871 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2872 menu
= MENU_GetMenu( hmenutmp
);
2874 /* Try to move 1 column left (if possible) */
2875 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2876 NO_SELECTED_ITEM
) {
2878 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2883 /* close topmost popup */
2884 while (hmenutmp
!= pmt
->hCurrentMenu
)
2886 hmenuprev
= hmenutmp
;
2887 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2890 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2891 pmt
->hCurrentMenu
= hmenuprev
;
2893 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2895 /* move menu bar selection if no more popups are left */
2897 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2898 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2900 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2902 /* A sublevel menu was displayed - display the next one
2903 * unless there is another displacement coming up */
2905 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2906 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2907 pmt
->hTopMenu
, TRUE
, wFlags
);
2913 /***********************************************************************
2916 * Handle a VK_RIGHT key event in a menu.
2918 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2921 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2924 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2926 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2927 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2929 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2931 /* If already displaying a popup, try to display sub-popup */
2933 hmenutmp
= pmt
->hCurrentMenu
;
2934 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2936 /* if subpopup was displayed then we are done */
2937 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2940 /* Check to see if there's another column */
2941 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2942 NO_SELECTED_ITEM
) {
2943 TRACE("Going to %d.\n", nextcol
);
2944 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2949 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2951 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2953 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2954 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2955 } else hmenutmp
= 0;
2957 /* try to move to the next item */
2958 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2959 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2961 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2962 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2963 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2964 pmt
->hTopMenu
, TRUE
, wFlags
);
2968 static void CALLBACK
release_capture( BOOL __normal
)
2970 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2973 /***********************************************************************
2976 * Menu tracking code.
2978 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2979 HWND hwnd
, const RECT
*lprect
)
2984 INT executedMenuId
= -1;
2986 BOOL enterIdleSent
= FALSE
;
2990 mt
.hCurrentMenu
= hmenu
;
2991 mt
.hTopMenu
= hmenu
;
2992 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2996 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2997 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3000 if (!(menu
= MENU_GetMenu( hmenu
)))
3002 WARN("Invalid menu handle %p\n", hmenu
);
3003 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3007 if (wFlags
& TPM_BUTTONDOWN
)
3009 /* Get the result in order to start the tracking or not */
3010 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3011 fEndMenu
= !fRemove
;
3014 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3016 /* owner may not be visible when tracking a popup, so use the menu itself */
3017 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3018 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3020 __TRY
while (!fEndMenu
)
3022 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3023 if (!menu
) /* sometimes happens if I do a window manager close */
3026 /* we have to keep the message in the queue until it's
3027 * clear that menu loop is not over yet. */
3031 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3033 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3034 /* remove the message from the queue */
3035 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3041 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
3042 enterIdleSent
= TRUE
;
3043 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3049 /* check if EndMenu() tried to cancel us, by posting this message */
3050 if(msg
.message
== WM_CANCELMODE
)
3052 /* we are now out of the loop */
3055 /* remove the message from the queue */
3056 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3058 /* break out of internal loop, ala ESCAPE */
3062 TranslateMessage( &msg
);
3065 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3066 enterIdleSent
=FALSE
;
3069 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3072 * Use the mouse coordinates in lParam instead of those in the MSG
3073 * struct to properly handle synthetic messages. They are already
3074 * in screen coordinates.
3076 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3077 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3079 /* Find a menu for this mouse event */
3080 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3084 /* no WM_NC... messages in captured state */
3086 case WM_RBUTTONDBLCLK
:
3087 case WM_RBUTTONDOWN
:
3088 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3090 case WM_LBUTTONDBLCLK
:
3091 case WM_LBUTTONDOWN
:
3092 /* If the message belongs to the menu, removes it from the queue */
3093 /* Else, end menu tracking */
3094 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3095 fEndMenu
= !fRemove
;
3099 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3102 /* Check if a menu was selected by the mouse */
3105 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3106 TRACE("executedMenuId %d\n", executedMenuId
);
3108 /* End the loop if executedMenuId is an item ID */
3109 /* or if the job was done (executedMenuId = 0). */
3110 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3112 /* No menu was selected by the mouse */
3113 /* if the function was called by TrackPopupMenu, continue
3114 with the menu tracking. If not, stop it */
3116 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3121 /* the selected menu item must be changed every time */
3122 /* the mouse moves. */
3125 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3127 } /* switch(msg.message) - mouse */
3129 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3131 fRemove
= TRUE
; /* Keyboard messages are always removed */
3144 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3145 NO_SELECTED_ITEM
, FALSE
, 0 );
3146 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3147 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3151 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3153 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3154 if (!(menu
->wFlags
& MF_POPUP
))
3155 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3156 else /* otherwise try to move selection */
3157 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3158 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3162 MENU_KeyLeft( &mt
, wFlags
);
3166 MENU_KeyRight( &mt
, wFlags
);
3170 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3176 hi
.cbSize
= sizeof(HELPINFO
);
3177 hi
.iContextType
= HELPINFO_MENUITEM
;
3178 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3181 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3182 hi
.hItemHandle
= hmenu
;
3183 hi
.dwContextId
= menu
->dwContextHelpID
;
3184 hi
.MousePos
= msg
.pt
;
3185 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3192 break; /* WM_KEYDOWN */
3199 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3201 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3202 fEndMenu
= (executedMenuId
!= -2);
3207 /* Hack to avoid control chars. */
3208 /* We will find a better way real soon... */
3209 if (msg
.wParam
< 32) break;
3211 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3212 LOWORD(msg
.wParam
), FALSE
);
3213 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3214 else if (pos
== (UINT
)-1) MessageBeep(0);
3217 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3219 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3220 fEndMenu
= (executedMenuId
!= -2);
3224 } /* switch(msg.message) - kbd */
3228 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3229 DispatchMessageW( &msg
);
3233 if (!fEndMenu
) fRemove
= TRUE
;
3235 /* finally remove message from the queue */
3237 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3238 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3239 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3241 __FINALLY( release_capture
)
3243 /* If dropdown is still painted and the close box is clicked on
3244 then the menu will be destroyed as part of the DispatchMessage above.
3245 This will then invalidate the menu handle in mt.hTopMenu. We should
3246 check for this first. */
3247 if( IsMenu( mt
.hTopMenu
) )
3249 menu
= MENU_GetMenu( mt
.hTopMenu
);
3251 if( IsWindow( mt
.hOwnerWnd
) )
3253 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3255 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3257 DestroyWindow( menu
->hWnd
);
3260 if (!(wFlags
& TPM_NONOTIFY
))
3261 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3262 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3264 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3265 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
3268 /* Reset the variable for hiding menu */
3269 if( menu
) menu
->bTimeToHide
= FALSE
;
3272 /* The return value is only used by TrackPopupMenu */
3273 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3274 if (executedMenuId
== -1) executedMenuId
= 0;
3275 return executedMenuId
;
3278 /***********************************************************************
3281 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3285 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3289 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3290 if (!(wFlags
& TPM_NONOTIFY
))
3291 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3293 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3295 if (!(wFlags
& TPM_NONOTIFY
))
3297 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3298 /* If an app changed/recreated menu bar entries in WM_INITMENU
3299 * menu sizes will be recalculated once the menu created/shown.
3303 /* This makes the menus of applications built with Delphi work.
3304 * It also enables menus to be displayed in more than one window,
3305 * but there are some bugs left that need to be fixed in this case.
3307 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3311 /***********************************************************************
3314 static BOOL
MENU_ExitTracking(HWND hWnd
)
3316 TRACE("hwnd=%p\n", hWnd
);
3318 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3324 /***********************************************************************
3325 * MENU_TrackMouseMenuBar
3327 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3329 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3331 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3332 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3334 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3338 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3339 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3340 MENU_ExitTracking(hWnd
);
3345 /***********************************************************************
3346 * MENU_TrackKbdMenuBar
3348 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3350 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3352 UINT uItem
= NO_SELECTED_ITEM
;
3354 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3356 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3358 /* find window that has a menu */
3360 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3361 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3363 /* check if we have to track a system menu */
3365 hTrackMenu
= GetMenu( hwnd
);
3366 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3368 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3369 hTrackMenu
= get_win_sys_menu( hwnd
);
3371 wParam
|= HTSYSMENU
; /* prevent item lookup */
3374 if (!IsMenu( hTrackMenu
)) return;
3376 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3378 if( wChar
&& wChar
!= ' ' )
3380 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3381 if ( uItem
>= (UINT
)(-2) )
3383 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3384 /* schedule end of menu tracking */
3385 wFlags
|= TF_ENDMENU
;
3390 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3392 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3394 if( uItem
== NO_SELECTED_ITEM
)
3395 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3397 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3401 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3402 MENU_ExitTracking( hwnd
);
3406 /**********************************************************************
3407 * TrackPopupMenu (USER32.@)
3409 * Like the win32 API, the function return the command ID only if the
3410 * flag TPM_RETURNCMD is on.
3413 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3414 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3418 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3419 hMenu
, wFlags
, x
, y
, nReserved
, hWnd
, wine_dbgstr_rect(lpRect
));
3421 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3423 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3424 if (!(wFlags
& TPM_NONOTIFY
))
3425 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3427 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3428 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3429 MENU_ExitTracking(hWnd
);
3434 /**********************************************************************
3435 * TrackPopupMenuEx (USER32.@)
3437 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3438 HWND hWnd
, LPTPMPARAMS lpTpm
)
3440 FIXME("not fully implemented\n" );
3441 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3442 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3445 /***********************************************************************
3448 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3450 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3452 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3458 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3459 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3463 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3464 return MA_NOACTIVATE
;
3469 BeginPaint( hwnd
, &ps
);
3470 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3471 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3472 EndPaint( hwnd
, &ps
);
3479 /* zero out global pointer in case resident popup window was destroyed. */
3480 if (hwnd
== top_popup
) top_popup
= 0;
3487 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3490 SetWindowLongPtrW( hwnd
, 0, 0 );
3493 case MM_SETMENUHANDLE
:
3494 SetWindowLongPtrW( hwnd
, 0, wParam
);
3497 case MM_GETMENUHANDLE
:
3498 return GetWindowLongPtrW( hwnd
, 0 );
3501 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3507 /***********************************************************************
3508 * MENU_GetMenuBarHeight
3510 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3512 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3513 INT orgX
, INT orgY
)
3519 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3521 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3523 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3524 SelectObject( hdc
, get_menu_font(FALSE
));
3525 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3526 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3527 ReleaseDC( hwnd
, hdc
);
3528 return lppop
->Height
;
3532 /*******************************************************************
3533 * ChangeMenuA (USER32.@)
3535 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3536 UINT id
, UINT flags
)
3538 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3539 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3541 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3542 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3544 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3545 flags
& MF_BYPOSITION
? pos
: id
,
3546 flags
& ~MF_REMOVE
);
3547 /* Default: MF_INSERT */
3548 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3552 /*******************************************************************
3553 * ChangeMenuW (USER32.@)
3555 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3556 UINT id
, UINT flags
)
3558 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3559 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3561 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3562 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3564 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3565 flags
& MF_BYPOSITION
? pos
: id
,
3566 flags
& ~MF_REMOVE
);
3567 /* Default: MF_INSERT */
3568 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3572 /*******************************************************************
3573 * CheckMenuItem (USER32.@)
3575 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3580 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3581 ret
= item
->fState
& MF_CHECKED
;
3582 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3583 else item
->fState
&= ~MF_CHECKED
;
3588 /**********************************************************************
3589 * EnableMenuItem (USER32.@)
3591 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3597 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3599 /* Get the Popupmenu to access the owner menu */
3600 if (!(menu
= MENU_GetMenu(hMenu
)))
3603 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3606 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3607 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3609 /* If the close item in the system menu change update the close button */
3610 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3612 if (menu
->hSysMenuOwner
!= 0)
3615 POPUPMENU
* parentMenu
;
3617 /* Get the parent menu to access*/
3618 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3621 /* Refresh the frame to reflect the change */
3622 GetWindowRect(parentMenu
->hWnd
, &rc
);
3623 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3625 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3633 /*******************************************************************
3634 * GetMenuStringA (USER32.@)
3636 INT WINAPI
GetMenuStringA(
3637 HMENU hMenu
, /* [in] menuhandle */
3638 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3639 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3640 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3641 UINT wFlags
/* [in] MF_ flags */
3645 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3646 if (str
&& nMaxSiz
) str
[0] = '\0';
3647 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3648 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3651 if (!item
->text
) return 0;
3652 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3653 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3655 TRACE("returning %s\n", debugstr_a(str
));
3660 /*******************************************************************
3661 * GetMenuStringW (USER32.@)
3663 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3664 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3668 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3669 if (str
&& nMaxSiz
) str
[0] = '\0';
3670 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3671 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3674 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3675 if( !(item
->text
)) {
3679 lstrcpynW( str
, item
->text
, nMaxSiz
);
3680 TRACE("returning %s\n", debugstr_w(str
));
3681 return strlenW(str
);
3685 /**********************************************************************
3686 * HiliteMenuItem (USER32.@)
3688 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3692 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3693 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3694 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3695 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3696 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3697 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3702 /**********************************************************************
3703 * GetMenuState (USER32.@)
3705 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3708 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3709 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3710 debug_print_menuitem (" item: ", item
, "");
3711 if (item
->fType
& MF_POPUP
)
3713 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3714 if (!menu
) return -1;
3715 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3719 /* We used to (from way back then) mask the result to 0xff. */
3720 /* I don't know why and it seems wrong as the documented */
3721 /* return flag MF_SEPARATOR is outside that mask. */
3722 return (item
->fType
| item
->fState
);
3727 /**********************************************************************
3728 * GetMenuItemCount (USER32.@)
3730 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3732 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3733 if (!menu
) return -1;
3734 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3735 return menu
->nItems
;
3739 /**********************************************************************
3740 * GetMenuItemID (USER32.@)
3742 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3746 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3747 if (lpmi
->fType
& MF_POPUP
) return -1;
3753 /*******************************************************************
3754 * InsertMenuW (USER32.@)
3756 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3757 UINT_PTR id
, LPCWSTR str
)
3761 if (IS_STRING_ITEM(flags
) && str
)
3762 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3763 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3764 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3765 hMenu
, pos
, flags
, id
, str
);
3767 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3769 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3771 RemoveMenu( hMenu
, pos
, flags
);
3775 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3780 /*******************************************************************
3781 * InsertMenuA (USER32.@)
3783 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3784 UINT_PTR id
, LPCSTR str
)
3788 if (IS_STRING_ITEM(flags
) && str
)
3790 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3791 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3794 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3795 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3796 HeapFree( GetProcessHeap(), 0, newstr
);
3800 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3804 /*******************************************************************
3805 * AppendMenuA (USER32.@)
3807 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3808 UINT_PTR id
, LPCSTR data
)
3810 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3814 /*******************************************************************
3815 * AppendMenuW (USER32.@)
3817 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3818 UINT_PTR id
, LPCWSTR data
)
3820 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3824 /**********************************************************************
3825 * RemoveMenu (USER32.@)
3827 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3832 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3833 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3834 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3838 MENU_FreeItemData( item
);
3840 if (--menu
->nItems
== 0)
3842 HeapFree( GetProcessHeap(), 0, menu
->items
);
3847 while(nPos
< menu
->nItems
)
3853 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3854 menu
->nItems
* sizeof(MENUITEM
) );
3860 /**********************************************************************
3861 * DeleteMenu (USER32.@)
3863 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3865 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3866 if (!item
) return FALSE
;
3867 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3868 /* nPos is now the position of the item */
3869 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3874 /*******************************************************************
3875 * ModifyMenuW (USER32.@)
3877 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3878 UINT_PTR id
, LPCWSTR str
)
3882 if (IS_STRING_ITEM(flags
))
3883 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3885 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3887 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3888 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3889 return MENU_SetItemData( item
, flags
, id
, str
);
3893 /*******************************************************************
3894 * ModifyMenuA (USER32.@)
3896 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3897 UINT_PTR id
, LPCSTR str
)
3901 if (IS_STRING_ITEM(flags
) && str
)
3903 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3904 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3907 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3908 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3909 HeapFree( GetProcessHeap(), 0, newstr
);
3913 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3917 /**********************************************************************
3918 * CreatePopupMenu (USER32.@)
3920 HMENU WINAPI
CreatePopupMenu(void)
3925 if (!(hmenu
= CreateMenu())) return 0;
3926 menu
= MENU_GetMenu( hmenu
);
3927 menu
->wFlags
|= MF_POPUP
;
3928 menu
->bTimeToHide
= FALSE
;
3933 /**********************************************************************
3934 * GetMenuCheckMarkDimensions (USER.417)
3935 * GetMenuCheckMarkDimensions (USER32.@)
3937 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3939 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3943 /**********************************************************************
3944 * SetMenuItemBitmaps (USER32.@)
3946 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3947 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3951 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3953 if (!hNewCheck
&& !hNewUnCheck
)
3955 item
->fState
&= ~MF_USECHECKBITMAPS
;
3957 else /* Install new bitmaps */
3959 item
->hCheckBit
= hNewCheck
;
3960 item
->hUnCheckBit
= hNewUnCheck
;
3961 item
->fState
|= MF_USECHECKBITMAPS
;
3967 /**********************************************************************
3968 * CreateMenu (USER32.@)
3970 HMENU WINAPI
CreateMenu(void)
3974 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3975 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3977 ZeroMemory(menu
, sizeof(POPUPMENU
));
3978 menu
->wMagic
= MENU_MAGIC
;
3979 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3980 menu
->bTimeToHide
= FALSE
;
3982 TRACE("return %p\n", hMenu
);
3988 /**********************************************************************
3989 * DestroyMenu (USER32.@)
3991 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3993 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3995 TRACE("(%p)\n", hMenu
);
3998 if (!lppop
) return FALSE
;
4000 lppop
->wMagic
= 0; /* Mark it as destroyed */
4002 /* DestroyMenu should not destroy system menu popup owner */
4003 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4005 DestroyWindow( lppop
->hWnd
);
4009 if (lppop
->items
) /* recursively destroy submenus */
4012 MENUITEM
*item
= lppop
->items
;
4013 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4015 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4016 MENU_FreeItemData( item
);
4018 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4020 USER_HEAP_FREE( hMenu
);
4025 /**********************************************************************
4026 * GetSystemMenu (USER32.@)
4028 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4030 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4033 if (wndPtr
== WND_DESKTOP
) return 0;
4034 if (wndPtr
== WND_OTHER_PROCESS
)
4036 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4040 if (wndPtr
->hSysMenu
&& bRevert
)
4042 DestroyMenu(wndPtr
->hSysMenu
);
4043 wndPtr
->hSysMenu
= 0;
4046 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4047 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4049 if( wndPtr
->hSysMenu
)
4052 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4054 /* Store the dummy sysmenu handle to facilitate the refresh */
4055 /* of the close button if the SC_CLOSE item change */
4056 menu
= MENU_GetMenu(retvalue
);
4058 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4060 WIN_ReleasePtr( wndPtr
);
4062 return bRevert
? 0 : retvalue
;
4066 /*******************************************************************
4067 * SetSystemMenu (USER32.@)
4069 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4071 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4073 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4075 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4076 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4077 WIN_ReleasePtr( wndPtr
);
4084 /**********************************************************************
4085 * GetMenu (USER32.@)
4087 HMENU WINAPI
GetMenu( HWND hWnd
)
4089 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4090 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4094 /**********************************************************************
4095 * GetMenuBarInfo (USER32.@)
4097 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4099 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4103 /**********************************************************************
4106 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4107 * SetWindowPos call that would result if SetMenu were called directly.
4109 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4111 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4113 if (hMenu
&& !IsMenu(hMenu
))
4115 WARN("hMenu %p is not a menu handle\n", hMenu
);
4118 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4121 hWnd
= WIN_GetFullHandle( hWnd
);
4122 if (GetCapture() == hWnd
)
4123 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4129 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4131 lpmenu
->hWnd
= hWnd
;
4132 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4134 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4139 /**********************************************************************
4140 * SetMenu (USER32.@)
4142 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4144 if(!MENU_SetMenu(hWnd
, hMenu
))
4147 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4148 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4153 /**********************************************************************
4154 * GetSubMenu (USER32.@)
4156 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4160 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4161 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4162 return lpmi
->hSubMenu
;
4166 /**********************************************************************
4167 * DrawMenuBar (USER32.@)
4169 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4172 HMENU hMenu
= GetMenu(hWnd
);
4174 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4176 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4178 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4179 lppop
->hwndOwner
= hWnd
;
4180 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4181 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4185 /***********************************************************************
4186 * DrawMenuBarTemp (USER32.@)
4190 * called by W98SE desk.cpl Control Panel Applet
4192 * Not 100% sure about the param names, but close.
4194 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4199 BOOL flat_menu
= FALSE
;
4201 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4204 hMenu
= GetMenu(hwnd
);
4207 hFont
= get_menu_font(FALSE
);
4209 lppop
= MENU_GetMenu( hMenu
);
4210 if (lppop
== NULL
|| lprect
== NULL
)
4212 retvalue
= GetSystemMetrics(SM_CYMENU
);
4216 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4218 hfontOld
= SelectObject( hDC
, hFont
);
4220 if (lppop
->Height
== 0)
4221 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4223 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4225 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4227 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4228 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4229 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4231 if (lppop
->nItems
== 0)
4233 retvalue
= GetSystemMetrics(SM_CYMENU
);
4237 for (i
= 0; i
< lppop
->nItems
; i
++)
4239 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4240 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4242 retvalue
= lppop
->Height
;
4245 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4249 /***********************************************************************
4250 * EndMenu (USER.187)
4251 * EndMenu (USER32.@)
4253 BOOL WINAPI
EndMenu(void)
4255 /* if we are in the menu code, and it is active */
4256 if (!fEndMenu
&& top_popup
)
4258 /* terminate the menu handling code */
4261 /* needs to be posted to wakeup the internal menu handler */
4262 /* which will now terminate the menu, in the event that */
4263 /* the main window was minimized, or lost focus, so we */
4264 /* don't end up with an orphaned menu */
4265 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4271 /***********************************************************************
4272 * LookupMenuHandle (USER.217)
4274 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4276 HMENU hmenu32
= HMENU_32(hmenu
);
4278 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4279 else return HMENU_16(hmenu32
);
4283 /**********************************************************************
4284 * LoadMenu (USER.150)
4286 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4292 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4293 if (!name
) return 0;
4295 instance
= GetExePtr( instance
);
4296 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4297 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4298 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4299 FreeResource16( handle
);
4304 /*****************************************************************
4305 * LoadMenuA (USER32.@)
4307 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4309 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4310 if (!hrsrc
) return 0;
4311 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
4315 /*****************************************************************
4316 * LoadMenuW (USER32.@)
4318 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4320 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4321 if (!hrsrc
) return 0;
4322 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
4326 /**********************************************************************
4327 * LoadMenuIndirect (USER.220)
4329 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4332 WORD version
, offset
;
4333 LPCSTR p
= (LPCSTR
)template;
4335 TRACE("(%p)\n", template );
4336 version
= GET_WORD(p
);
4340 WARN("version must be 0 for Win16\n" );
4343 offset
= GET_WORD(p
);
4344 p
+= sizeof(WORD
) + offset
;
4345 if (!(hMenu
= CreateMenu())) return 0;
4346 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4348 DestroyMenu( hMenu
);
4351 return HMENU_16(hMenu
);
4355 /**********************************************************************
4356 * LoadMenuIndirectW (USER32.@)
4358 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4361 WORD version
, offset
;
4362 LPCSTR p
= (LPCSTR
)template;
4364 version
= GET_WORD(p
);
4366 TRACE("%p, ver %d\n", template, version
);
4369 case 0: /* standard format is version of 0 */
4370 offset
= GET_WORD(p
);
4371 p
+= sizeof(WORD
) + offset
;
4372 if (!(hMenu
= CreateMenu())) return 0;
4373 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4375 DestroyMenu( hMenu
);
4379 case 1: /* extended format is version of 1 */
4380 offset
= GET_WORD(p
);
4381 p
+= sizeof(WORD
) + offset
;
4382 if (!(hMenu
= CreateMenu())) return 0;
4383 if (!MENUEX_ParseResource( p
, hMenu
))
4385 DestroyMenu( hMenu
);
4390 ERR("version %d not supported.\n", version
);
4396 /**********************************************************************
4397 * LoadMenuIndirectA (USER32.@)
4399 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4401 return LoadMenuIndirectW( template );
4405 /**********************************************************************
4408 BOOL WINAPI
IsMenu(HMENU hmenu
)
4410 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4414 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4420 /**********************************************************************
4421 * GetMenuItemInfo_common
4424 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4425 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4427 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4429 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4432 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4436 if( lpmii
->fMask
& MIIM_TYPE
) {
4437 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4438 WARN("invalid combination of fMask bits used\n");
4439 /* this does not happen on Win9x/ME */
4440 SetLastError( ERROR_INVALID_PARAMETER
);
4443 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4444 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4445 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4446 if( lpmii
->fType
& MFT_BITMAP
) {
4447 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4449 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4450 /* this does not happen on Win9x/ME */
4451 lpmii
->dwTypeData
= 0;
4456 /* copy the text string */
4457 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4459 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4462 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4464 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4470 len
= strlenW(menu
->text
);
4471 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4472 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4476 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4477 0, NULL
, NULL
) - 1;
4478 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4479 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4480 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4481 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4483 /* if we've copied a substring we return its length */
4484 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4485 if (lpmii
->cch
<= len
+ 1)
4490 /* return length of string */
4491 /* not on Win9x/ME if fType & MFT_BITMAP */
4497 if (lpmii
->fMask
& MIIM_FTYPE
)
4498 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4500 if (lpmii
->fMask
& MIIM_BITMAP
)
4501 lpmii
->hbmpItem
= menu
->hbmpItem
;
4503 if (lpmii
->fMask
& MIIM_STATE
)
4504 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4506 if (lpmii
->fMask
& MIIM_ID
)
4507 lpmii
->wID
= menu
->wID
;
4509 if (lpmii
->fMask
& MIIM_SUBMENU
)
4510 lpmii
->hSubMenu
= menu
->hSubMenu
;
4512 /* hSubMenu is always cleared
4513 * (not on Win9x/ME ) */
4514 lpmii
->hSubMenu
= 0;
4517 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4518 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4519 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4521 if (lpmii
->fMask
& MIIM_DATA
)
4522 lpmii
->dwItemData
= menu
->dwItemData
;
4527 /**********************************************************************
4528 * GetMenuItemInfoA (USER32.@)
4530 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4531 LPMENUITEMINFOA lpmii
)
4535 if( lpmii
->cbSize
!= sizeof( mii
) &&
4536 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4537 SetLastError( ERROR_INVALID_PARAMETER
);
4540 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4541 mii
.cbSize
= sizeof( mii
);
4542 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4543 (LPMENUITEMINFOW
)&mii
, FALSE
);
4544 mii
.cbSize
= lpmii
->cbSize
;
4545 memcpy( lpmii
, &mii
, mii
.cbSize
);
4549 /**********************************************************************
4550 * GetMenuItemInfoW (USER32.@)
4552 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4553 LPMENUITEMINFOW lpmii
)
4557 if( lpmii
->cbSize
!= sizeof( mii
) &&
4558 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4559 SetLastError( ERROR_INVALID_PARAMETER
);
4562 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4563 mii
.cbSize
= sizeof( mii
);
4564 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4565 mii
.cbSize
= lpmii
->cbSize
;
4566 memcpy( lpmii
, &mii
, mii
.cbSize
);
4571 /* set a menu item text from a ASCII or Unicode string */
4572 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4578 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4579 strcpyW( menu
->text
, text
);
4583 LPCSTR str
= (LPCSTR
)text
;
4584 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4585 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4586 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4591 /**********************************************************************
4592 * SetMenuItemInfo_common
4595 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4596 const MENUITEMINFOW
*lpmii
,
4599 if (!menu
) return FALSE
;
4601 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4603 if (lpmii
->fMask
& MIIM_TYPE
) {
4604 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4605 WARN("invalid combination of fMask bits used\n");
4606 /* this does not happen on Win9x/ME */
4607 SetLastError( ERROR_INVALID_PARAMETER
);
4611 /* Remove the old type bits and replace them with the new ones */
4612 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4613 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4615 if (IS_STRING_ITEM(menu
->fType
)) {
4616 HeapFree(GetProcessHeap(), 0, menu
->text
);
4617 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4618 } else if( (menu
->fType
) & MFT_BITMAP
)
4619 menu
->hbmpItem
= HBITMAP_32(LOWORD(lpmii
->dwTypeData
));
4622 if (lpmii
->fMask
& MIIM_FTYPE
) {
4623 if(( lpmii
->fType
& MFT_BITMAP
)) {
4624 SetLastError( ERROR_INVALID_PARAMETER
);
4627 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4628 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4630 if (lpmii
->fMask
& MIIM_STRING
) {
4631 /* free the string when used */
4632 HeapFree(GetProcessHeap(), 0, menu
->text
);
4633 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4636 if (lpmii
->fMask
& MIIM_STATE
)
4638 /* Other menu items having MFS_DEFAULT are not converted
4640 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4643 if (lpmii
->fMask
& MIIM_ID
)
4644 menu
->wID
= lpmii
->wID
;
4646 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4647 menu
->hSubMenu
= lpmii
->hSubMenu
;
4648 if (menu
->hSubMenu
) {
4649 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4651 subMenu
->wFlags
|= MF_POPUP
;
4652 menu
->fType
|= MF_POPUP
;
4655 SetLastError( ERROR_INVALID_PARAMETER
);
4660 menu
->fType
&= ~MF_POPUP
;
4663 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4665 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4666 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4668 if (lpmii
->fMask
& MIIM_DATA
)
4669 menu
->dwItemData
= lpmii
->dwItemData
;
4671 if (lpmii
->fMask
& MIIM_BITMAP
)
4672 menu
->hbmpItem
= lpmii
->hbmpItem
;
4674 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4675 menu
->fType
|= MFT_SEPARATOR
;
4677 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4681 /**********************************************************************
4682 * SetMenuItemInfoA (USER32.@)
4684 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4685 const MENUITEMINFOA
*lpmii
)
4689 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4691 if( lpmii
->cbSize
!= sizeof( mii
) &&
4692 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4693 SetLastError( ERROR_INVALID_PARAMETER
);
4696 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4697 if( lpmii
->cbSize
!= sizeof( mii
)) {
4698 mii
.cbSize
= sizeof( mii
);
4699 mii
.hbmpItem
= NULL
;
4701 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4702 (const MENUITEMINFOW
*)&mii
, FALSE
);
4705 /**********************************************************************
4706 * SetMenuItemInfoW (USER32.@)
4708 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4709 const MENUITEMINFOW
*lpmii
)
4713 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4715 if( lpmii
->cbSize
!= sizeof( mii
) &&
4716 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4717 SetLastError( ERROR_INVALID_PARAMETER
);
4720 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4721 if( lpmii
->cbSize
!= sizeof( mii
)) {
4722 mii
.cbSize
= sizeof( mii
);
4723 mii
.hbmpItem
= NULL
;
4725 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4726 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4729 /**********************************************************************
4730 * SetMenuDefaultItem (USER32.@)
4733 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4739 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4741 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4743 /* reset all default-item flags */
4745 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4747 item
->fState
&= ~MFS_DEFAULT
;
4750 /* no default item */
4759 if ( uItem
>= menu
->nItems
) return FALSE
;
4760 item
[uItem
].fState
|= MFS_DEFAULT
;
4765 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4767 if (item
->wID
== uItem
)
4769 item
->fState
|= MFS_DEFAULT
;
4778 /**********************************************************************
4779 * GetMenuDefaultItem (USER32.@)
4781 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4787 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4789 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4791 /* find default item */
4795 if (! item
) return -1;
4797 while ( !( item
->fState
& MFS_DEFAULT
) )
4800 if (i
>= menu
->nItems
) return -1;
4803 /* default: don't return disabled items */
4804 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4806 /* search rekursiv when needed */
4807 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4810 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4811 if ( -1 != ret
) return ret
;
4813 /* when item not found in submenu, return the popup item */
4815 return ( bypos
) ? i
: item
->wID
;
4820 /**********************************************************************
4821 * InsertMenuItemA (USER32.@)
4823 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4824 const MENUITEMINFOA
*lpmii
)
4829 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4831 if( lpmii
->cbSize
!= sizeof( mii
) &&
4832 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4833 SetLastError( ERROR_INVALID_PARAMETER
);
4836 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4837 if( lpmii
->cbSize
!= sizeof( mii
)) {
4838 mii
.cbSize
= sizeof( mii
);
4839 mii
.hbmpItem
= NULL
;
4842 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4843 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4847 /**********************************************************************
4848 * InsertMenuItemW (USER32.@)
4850 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4851 const MENUITEMINFOW
*lpmii
)
4856 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4858 if( lpmii
->cbSize
!= sizeof( mii
) &&
4859 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4860 SetLastError( ERROR_INVALID_PARAMETER
);
4863 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4864 if( lpmii
->cbSize
!= sizeof( mii
)) {
4865 mii
.cbSize
= sizeof( mii
);
4866 mii
.hbmpItem
= NULL
;
4869 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4870 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4873 /**********************************************************************
4874 * CheckMenuRadioItem (USER32.@)
4877 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4878 UINT first
, UINT last
, UINT check
,
4883 MENUITEM
*mi_first
= NULL
, *mi_check
;
4884 HMENU m_first
, m_check
;
4886 for (i
= first
; i
<= last
; i
++)
4893 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4894 if (!mi_first
) continue;
4895 mi_check
= mi_first
;
4901 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4902 if (!mi_check
) continue;
4905 if (m_first
!= m_check
) continue;
4906 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4910 mi_check
->fType
|= MFT_RADIOCHECK
;
4911 mi_check
->fState
|= MFS_CHECKED
;
4916 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4917 mi_check
->fState
&= ~MFS_CHECKED
;
4925 /**********************************************************************
4926 * GetMenuItemRect (USER32.@)
4928 * ATTENTION: Here, the returned values in rect are the screen
4929 * coordinates of the item just like if the menu was
4930 * always on the upper left side of the application.
4933 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4936 POPUPMENU
*itemMenu
;
4940 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4942 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4943 referenceHwnd
= hwnd
;
4947 itemMenu
= MENU_GetMenu(hMenu
);
4948 if (itemMenu
== NULL
)
4951 if(itemMenu
->hWnd
== 0)
4953 referenceHwnd
= itemMenu
->hWnd
;
4956 if ((rect
== NULL
) || (item
== NULL
))
4961 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4967 /**********************************************************************
4968 * SetMenuInfo (USER32.@)
4971 * MIM_APPLYTOSUBMENUS
4972 * actually use the items to draw the menu
4974 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4978 TRACE("(%p %p)\n", hMenu
, lpmi
);
4980 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4983 if (lpmi
->fMask
& MIM_BACKGROUND
)
4984 menu
->hbrBack
= lpmi
->hbrBack
;
4986 if (lpmi
->fMask
& MIM_HELPID
)
4987 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4989 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4990 menu
->cyMax
= lpmi
->cyMax
;
4992 if (lpmi
->fMask
& MIM_MENUDATA
)
4993 menu
->dwMenuData
= lpmi
->dwMenuData
;
4995 if (lpmi
->fMask
& MIM_STYLE
)
4997 menu
->dwStyle
= lpmi
->dwStyle
;
4998 if (menu
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
4999 if (menu
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5000 if (menu
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5008 /**********************************************************************
5009 * GetMenuInfo (USER32.@)
5015 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5018 TRACE("(%p %p)\n", hMenu
, lpmi
);
5020 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
5023 if (lpmi
->fMask
& MIM_BACKGROUND
)
5024 lpmi
->hbrBack
= menu
->hbrBack
;
5026 if (lpmi
->fMask
& MIM_HELPID
)
5027 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5029 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5030 lpmi
->cyMax
= menu
->cyMax
;
5032 if (lpmi
->fMask
& MIM_MENUDATA
)
5033 lpmi
->dwMenuData
= menu
->dwMenuData
;
5035 if (lpmi
->fMask
& MIM_STYLE
)
5036 lpmi
->dwStyle
= menu
->dwStyle
;
5044 /**********************************************************************
5045 * SetMenuContextHelpId (USER32.@)
5047 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5051 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5053 if ((menu
= MENU_GetMenu(hMenu
)))
5055 menu
->dwContextHelpID
= dwContextHelpID
;
5062 /**********************************************************************
5063 * GetMenuContextHelpId (USER32.@)
5065 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5069 TRACE("(%p)\n", hMenu
);
5071 if ((menu
= MENU_GetMenu(hMenu
)))
5073 return menu
->dwContextHelpID
;
5078 /**********************************************************************
5079 * MenuItemFromPoint (USER32.@)
5081 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5083 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5086 /*FIXME: Do we have to handle hWnd here? */
5087 if (!menu
) return -1;
5088 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5093 /**********************************************************************
5094 * translate_accelerator
5096 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5097 BYTE fVirt
, WORD key
, WORD cmd
)
5102 if (wParam
!= key
) return FALSE
;
5104 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5105 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5106 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5108 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5110 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5112 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5118 if(fVirt
& FVIRTKEY
)
5120 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5121 wParam
, 0xff & HIWORD(lParam
));
5123 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5124 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5128 if (!(lParam
& 0x01000000)) /* no special_key */
5130 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5131 { /* ^^ ALT pressed */
5132 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5141 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5145 HMENU hMenu
, hSubMenu
, hSysMenu
;
5146 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5148 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5149 hSysMenu
= get_win_sys_menu( hWnd
);
5151 /* find menu item and ask application to initialize it */
5152 /* 1. in the system menu */
5153 hSubMenu
= hSysMenu
;
5155 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5159 if (!IsWindowEnabled(hWnd
))
5163 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5164 if(hSubMenu
!= hSysMenu
)
5166 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5167 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5168 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5170 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5173 else /* 2. in the window's menu */
5177 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5181 if (!IsWindowEnabled(hWnd
))
5185 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5186 if(hSubMenu
!= hMenu
)
5188 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5189 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5190 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5192 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5199 if (uSysStat
!= (UINT
)-1)
5201 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5208 if (uStat
!= (UINT
)-1)
5214 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5226 if( mesg
==WM_COMMAND
)
5228 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5229 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5231 else if( mesg
==WM_SYSCOMMAND
)
5233 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5234 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5238 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5239 * #0: unknown (please report!)
5240 * #1: for WM_KEYUP,WM_SYSKEYUP
5241 * #2: mouse is captured
5242 * #3: window is disabled
5243 * #4: it's a disabled system menu option
5244 * #5: it's a menu option, but window is iconic
5245 * #6: it's a menu option, but disabled
5247 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5249 ERR_(accel
)(" unknown reason - please report!\n");
5254 /**********************************************************************
5255 * TranslateAcceleratorA (USER32.@)
5256 * TranslateAccelerator (USER32.@)
5258 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5261 LPACCEL16 lpAccelTbl
;
5265 if (!hWnd
|| !msg
) return 0;
5267 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5269 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5273 wParam
= msg
->wParam
;
5275 switch (msg
->message
)
5284 char ch
= LOWORD(wParam
);
5286 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5287 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5295 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5296 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5300 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5301 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5303 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5308 /**********************************************************************
5309 * TranslateAcceleratorW (USER32.@)
5311 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5314 LPACCEL16 lpAccelTbl
;
5317 if (!hWnd
|| !msg
) return 0;
5319 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5321 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5325 switch (msg
->message
)
5337 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5338 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5342 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5343 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5345 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);