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"
61 #include "user_private.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
65 WINE_DECLARE_DEBUG_CHANNEL(accel
);
67 /* internal popup menu window messages */
69 #define MM_SETMENUHANDLE (WM_USER + 0)
70 #define MM_GETMENUHANDLE (WM_USER + 1)
72 /* Menu item structure */
74 /* ----------- MENUITEMINFO Stuff ----------- */
75 UINT fType
; /* Item type. */
76 UINT fState
; /* Item state. */
77 UINT_PTR wID
; /* Item id. */
78 HMENU hSubMenu
; /* Pop-up menu. */
79 HBITMAP hCheckBit
; /* Bitmap when checked. */
80 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
81 LPWSTR text
; /* Item text. */
82 ULONG_PTR dwItemData
; /* Application defined. */
83 LPWSTR dwTypeData
; /* depends on fMask */
84 HBITMAP hbmpItem
; /* bitmap */
85 /* ----------- Wine stuff ----------- */
86 RECT rect
; /* Item area (relative to menu window) */
87 UINT xTab
; /* X position of text after Tab */
88 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
92 /* Popup menu structure */
94 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
95 WORD wMagic
; /* Magic number */
96 WORD Width
; /* Width of the whole menu */
97 WORD Height
; /* Height of the whole menu */
98 UINT nItems
; /* Number of items in the menu */
99 HWND hWnd
; /* Window containing the menu */
100 MENUITEM
*items
; /* Array of menu items */
101 UINT FocusedItem
; /* Currently focused item */
102 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
103 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
104 BOOL bScrolling
; /* Scroll arrows are active */
105 UINT nScrollPos
; /* Current scroll position */
106 UINT nTotalHeight
; /* Total height of menu items inside menu */
107 /* ------------ MENUINFO members ------ */
108 DWORD dwStyle
; /* Extended menu style */
109 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
110 HBRUSH hbrBack
; /* brush for menu background */
111 DWORD dwContextHelpID
;
112 DWORD dwMenuData
; /* application defined value */
113 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
114 SIZE maxBmpSize
; /* Maximum size of the bitmap items */
115 } POPUPMENU
, *LPPOPUPMENU
;
117 /* internal flags for menu tracking */
119 #define TF_ENDMENU 0x10000
120 #define TF_SUSPENDPOPUP 0x20000
121 #define TF_SKIPREMOVE 0x40000
126 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
127 HMENU hTopMenu
; /* initial menu */
128 HWND hOwnerWnd
; /* where notifications are sent */
132 #define MENU_MAGIC 0x554d /* 'MU' */
137 /* Internal MENU_TrackMenu() flags */
138 #define TPM_INTERNAL 0xF0000000
139 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
140 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
141 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
143 /* Space between 2 columns */
144 #define MENU_COL_SPACE 4
146 /* top and bottom margins for popup menus */
147 #define MENU_TOP_MARGIN 3
148 #define MENU_BOTTOM_MARGIN 2
150 /* (other menu->FocusedItem values give the position of the focused item) */
151 #define NO_SELECTED_ITEM 0xffff
153 #define MENU_ITEM_TYPE(flags) \
154 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
156 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
157 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
158 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
160 #define IS_SYSTEM_MENU(menu) \
161 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
163 #define MENUITEMINFO_TYPE_MASK \
164 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
165 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
166 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
167 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
168 #define STATE_MASK (~TYPE_MASK)
169 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
171 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
173 static SIZE menucharsize
;
174 static UINT ODitemheight
; /* default owner drawn item height */
176 /* Use global popup window because there's no way 2 menus can
177 * be tracked at the same time. */
178 static HWND top_popup
;
180 /* Flag set by EndMenu() to force an exit from menu tracking */
181 static BOOL fEndMenu
= FALSE
;
183 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
185 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
187 /*********************************************************************
188 * menu class descriptor
190 const struct builtin_class_descr MENU_builtin_class
=
192 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
193 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
194 NULL
, /* procA (winproc is Unicode only) */
195 PopupMenuWndProc
, /* procW */
196 sizeof(HMENU
), /* extra */
197 IDC_ARROW
, /* cursor */
198 (HBRUSH
)(COLOR_MENU
+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
222 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix
);
228 UINT flags
= mp
->fType
;
229 TRACE( "{ ID=0x%lx", mp
->wID
);
231 TRACE( ", Sub=%p", mp
->hSubMenu
);
235 MENUFLAG( MFT_SEPARATOR
, "sep");
236 MENUFLAG( MFT_OWNERDRAW
, "own");
237 MENUFLAG( MFT_BITMAP
, "bit");
238 MENUFLAG(MF_POPUP
, "pop");
239 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
240 MENUFLAG(MFT_MENUBREAK
, "brk");
241 MENUFLAG(MFT_RADIOCHECK
, "radio");
242 MENUFLAG(MFT_RIGHTORDER
, "rorder");
243 MENUFLAG(MF_SYSMENU
, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
246 TRACE( "+0x%x", flags
);
252 MENUFLAG(MFS_GRAYED
, "grey");
253 MENUFLAG(MFS_DEFAULT
, "default");
254 MENUFLAG(MFS_DISABLED
, "dis");
255 MENUFLAG(MFS_CHECKED
, "check");
256 MENUFLAG(MFS_HILITE
, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
258 MENUFLAG(MF_MOUSESELECT
, "mouse");
260 TRACE( "+0x%x", flags
);
263 TRACE( ", Chk=%p", mp
->hCheckBit
);
265 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
267 TRACE( ", Text=%s", debugstr_w(mp
->text
));
269 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
272 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
273 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
275 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
280 TRACE(" %s\n", postfix
);
287 /***********************************************************************
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
294 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
295 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
297 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
303 /***********************************************************************
306 * Get the system menu of a window
308 static HMENU
get_win_sys_menu( HWND hwnd
)
311 WND
*win
= WIN_GetPtr( hwnd
);
312 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
315 WIN_ReleasePtr( win
);
320 /***********************************************************************
323 static HFONT
get_menu_font( BOOL bold
)
325 static HFONT hMenuFont
, hMenuFontBold
;
327 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
331 NONCLIENTMETRICSW ncm
;
334 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
335 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
339 ncm
.lfMenuFont
.lfWeight
+= 300;
340 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
342 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
343 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
347 /* another thread beat us to it */
355 /***********************************************************************
358 static HBITMAP
get_arrow_bitmap(void)
360 static HBITMAP arrow_bitmap
;
362 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
366 /***********************************************************************
367 * get_down_arrow_bitmap
369 static HBITMAP
get_down_arrow_bitmap(void)
371 static HBITMAP arrow_bitmap
;
373 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
377 /***********************************************************************
378 * get_down_arrow_inactive_bitmap
380 static HBITMAP
get_down_arrow_inactive_bitmap(void)
382 static HBITMAP arrow_bitmap
;
384 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
388 /***********************************************************************
389 * get_up_arrow_bitmap
391 static HBITMAP
get_up_arrow_bitmap(void)
393 static HBITMAP arrow_bitmap
;
395 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
399 /***********************************************************************
400 * get_up_arrow_inactive_bitmap
402 static HBITMAP
get_up_arrow_inactive_bitmap(void)
404 static HBITMAP arrow_bitmap
;
406 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
410 /***********************************************************************
413 * Return the default system menu.
415 static HMENU
MENU_CopySysPopup(void)
417 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
418 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
421 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
422 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
423 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
426 ERR("Unable to load default system menu\n" );
428 TRACE("returning %p.\n", hMenu
);
434 /**********************************************************************
437 * Create a copy of the system menu. System menu in Windows is
438 * a special menu bar with the single entry - system menu popup.
439 * This popup is presented to the outside world as a "system menu".
440 * However, the real system menu handle is sometimes seen in the
441 * WM_MENUSELECT parameters (and Word 6 likes it this way).
443 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
447 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
448 if ((hMenu
= CreateMenu()))
450 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
451 menu
->wFlags
= MF_SYSMENU
;
452 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
453 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
456 hPopupMenu
= MENU_CopySysPopup();
460 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
461 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
463 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
464 (UINT_PTR
)hPopupMenu
, NULL
);
466 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
467 menu
->items
[0].fState
= 0;
468 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
470 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
473 DestroyMenu( hMenu
);
475 ERR("failed to load system menu!\n");
480 /***********************************************************************
481 * MENU_InitSysMenuPopup
483 * Grey the appropriate items in System menu.
485 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
489 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
490 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
491 gray
= ((style
& WS_MAXIMIZE
) != 0);
492 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
493 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
494 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
495 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
496 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
497 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
498 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
499 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
501 /* The menu item must keep its state if it's disabled */
503 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
507 /******************************************************************************
509 * UINT MENU_GetStartOfNextColumn(
512 *****************************************************************************/
514 static UINT
MENU_GetStartOfNextColumn(
517 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
521 return NO_SELECTED_ITEM
;
523 i
= menu
->FocusedItem
+ 1;
524 if( i
== NO_SELECTED_ITEM
)
527 for( ; i
< menu
->nItems
; ++i
) {
528 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
532 return NO_SELECTED_ITEM
;
536 /******************************************************************************
538 * UINT MENU_GetStartOfPrevColumn(
541 *****************************************************************************/
543 static UINT
MENU_GetStartOfPrevColumn(
546 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
550 return NO_SELECTED_ITEM
;
552 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
553 return NO_SELECTED_ITEM
;
555 /* Find the start of the column */
557 for(i
= menu
->FocusedItem
; i
!= 0 &&
558 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
562 return NO_SELECTED_ITEM
;
564 for(--i
; i
!= 0; --i
) {
565 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
569 TRACE("ret %d.\n", i
);
576 /***********************************************************************
579 * Find a menu item. Return a pointer on the item, and modifies *hmenu
580 * in case the item was in a sub-menu.
582 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
585 MENUITEM
*fallback
= NULL
;
586 UINT fallback_pos
= 0;
589 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
590 if (wFlags
& MF_BYPOSITION
)
592 if (*nPos
>= menu
->nItems
) return NULL
;
593 return &menu
->items
[*nPos
];
597 MENUITEM
*item
= menu
->items
;
598 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
600 if (item
->fType
& MF_POPUP
)
602 HMENU hsubmenu
= item
->hSubMenu
;
603 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
609 else if (item
->wID
== *nPos
)
611 /* fallback to this item if nothing else found */
616 else if (item
->wID
== *nPos
)
625 *nPos
= fallback_pos
;
630 /***********************************************************************
633 * Find a Sub menu. Return the position of the submenu, and modifies
634 * *hmenu in case it is found in another sub-menu.
635 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
637 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
642 if (((*hmenu
)==(HMENU
)0xffff) ||
643 (!(menu
= MENU_GetMenu(*hmenu
))))
644 return NO_SELECTED_ITEM
;
646 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
647 if(!(item
->fType
& MF_POPUP
)) continue;
648 if (item
->hSubMenu
== hSubTarget
) {
652 HMENU hsubmenu
= item
->hSubMenu
;
653 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
654 if (pos
!= NO_SELECTED_ITEM
) {
660 return NO_SELECTED_ITEM
;
663 /***********************************************************************
666 static void MENU_FreeItemData( MENUITEM
* item
)
669 HeapFree( GetProcessHeap(), 0, item
->text
);
672 /***********************************************************************
673 * MENU_AdjustMenuItemRect
675 * Adjust menu item rectangle according to scrolling state.
678 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
680 if (menu
->bScrolling
)
682 UINT arrow_bitmap_height
;
685 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
686 arrow_bitmap_height
= bmp
.bmHeight
;
687 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
688 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
693 /***********************************************************************
694 * MENU_FindItemByCoords
696 * Find the item at the specified coordinates (screen coords). Does
697 * not work for child windows and therefore should not be called for
698 * an arbitrary system menu.
700 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
701 POINT pt
, UINT
*pos
)
707 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
711 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
714 MENU_AdjustMenuItemRect(menu
, &rect
);
715 if (PtInRect(&rect
, pt
))
725 /***********************************************************************
728 * Find the menu item selected by a key press.
729 * Return item id, -1 if none, -2 if we should close the menu.
731 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
732 WCHAR key
, BOOL forceMenuChar
)
734 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
736 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
740 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
741 MENUITEM
*item
= menu
->items
;
748 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
752 WCHAR
*p
= item
->text
- 2;
755 p
= strchrW (p
+ 2, '&');
757 while (p
!= NULL
&& p
[1] == '&');
758 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
762 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
763 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
764 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
765 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
771 /***********************************************************************
772 * MENU_GetBitmapItemSize
774 * Get the size of a bitmap item.
776 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
780 HBITMAP bmp
= lpitem
->hbmpItem
;
782 size
->cx
= size
->cy
= 0;
784 /* check if there is a magic menu item associated with this item */
785 switch( (INT_PTR
) bmp
)
787 case (INT_PTR
)HBMMENU_CALLBACK
:
789 MEASUREITEMSTRUCT measItem
;
790 measItem
.CtlType
= ODT_MENU
;
792 measItem
.itemID
= lpitem
->wID
;
793 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
794 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
795 measItem
.itemData
= lpitem
->dwItemData
;
796 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
797 size
->cx
= measItem
.itemWidth
;
798 size
->cy
= measItem
.itemHeight
;
802 case (INT_PTR
)HBMMENU_SYSTEM
:
803 if (lpitem
->dwItemData
)
805 bmp
= (HBITMAP
)lpitem
->dwItemData
;
809 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
810 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
811 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
812 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
813 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
814 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
817 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
818 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
819 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
820 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
821 FIXME("Magic %p not implemented\n", bmp
);
824 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
826 size
->cx
= bm
.bmWidth
;
827 size
->cy
= bm
.bmHeight
;
831 /***********************************************************************
832 * MENU_DrawBitmapItem
834 * Draw a bitmap item.
836 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
837 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
843 int w
= rect
->right
- rect
->left
;
844 int h
= rect
->bottom
- rect
->top
;
847 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
850 /* Check if there is a magic menu item associated with this item */
851 if (IS_MAGIC_BITMAP(hbmToDraw
))
856 switch((INT_PTR
)hbmToDraw
)
858 case (INT_PTR
)HBMMENU_SYSTEM
:
859 if (lpitem
->dwItemData
)
861 bmp
= (HBITMAP
)lpitem
->dwItemData
;
862 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
866 static HBITMAP hBmpSysMenu
;
868 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
870 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
871 /* only use right half of the bitmap */
872 bmp_xoffset
= bm
.bmWidth
/ 2;
873 bm
.bmWidth
-= bmp_xoffset
;
876 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
877 flags
= DFCS_CAPTIONRESTORE
;
879 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
880 flags
= DFCS_CAPTIONMIN
;
882 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
883 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
885 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
886 flags
= DFCS_CAPTIONCLOSE
;
888 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
889 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
891 case (INT_PTR
)HBMMENU_CALLBACK
:
893 DRAWITEMSTRUCT drawItem
;
894 drawItem
.CtlType
= ODT_MENU
;
896 drawItem
.itemID
= lpitem
->wID
;
897 drawItem
.itemAction
= odaction
;
898 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
899 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
900 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
901 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
902 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
903 drawItem
.hwndItem
= (HWND
)hmenu
;
905 drawItem
.itemData
= lpitem
->dwItemData
;
906 drawItem
.rcItem
= *rect
;
907 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
911 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
912 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
913 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
914 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
916 FIXME("Magic %p not implemented\n", hbmToDraw
);
920 InflateRect( &r
, -1, -1 );
921 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
922 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
926 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
929 hdcMem
= CreateCompatibleDC( hdc
);
930 SelectObject( hdcMem
, bmp
);
932 /* handle fontsize > bitmap_height */
933 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
935 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
936 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
937 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
938 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
943 /***********************************************************************
946 * Calculate the size of the menu item and store it in lpitem->rect.
948 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
949 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
952 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
953 UINT arrow_bitmap_width
;
957 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
958 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
959 (menuBar
? " (MenuBar)" : ""));
961 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
962 arrow_bitmap_width
= bm
.bmWidth
;
964 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
965 if( !menucharsize
.cx
) {
966 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
967 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
968 * but it is unlikely an application will depend on that */
969 ODitemheight
= HIWORD( GetDialogBaseUnits());
972 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
974 if (lpitem
->fType
& MF_OWNERDRAW
)
976 MEASUREITEMSTRUCT mis
;
977 mis
.CtlType
= ODT_MENU
;
979 mis
.itemID
= lpitem
->wID
;
980 mis
.itemData
= lpitem
->dwItemData
;
981 mis
.itemHeight
= ODitemheight
;
983 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
984 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
985 * width of a menufont character to the width of an owner-drawn menu.
987 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
989 /* under at least win95 you seem to be given a standard
990 height for the menu and the height value is ignored */
991 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
993 lpitem
->rect
.bottom
+= mis
.itemHeight
;
995 TRACE("id=%04lx size=%dx%d\n",
996 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
997 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1001 if (lpitem
->fType
& MF_SEPARATOR
)
1003 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1005 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1013 if (lpitem
->hbmpItem
) {
1016 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1017 /* Keep the size of the bitmap in callback mode to be able
1018 * to draw it correctly */
1019 lpitem
->bmpsize
= size
;
1020 lppop
->maxBmpSize
.cx
= max( lppop
->maxBmpSize
.cx
, size
.cx
);
1021 lppop
->maxBmpSize
.cy
= max( lppop
->maxBmpSize
.cy
, size
.cy
);
1022 lpitem
->rect
.right
+= size
.cx
+ 2;
1023 itemheight
= size
.cy
+ 2;
1025 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1026 lpitem
->rect
.right
+= check_bitmap_width
;
1027 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1028 lpitem
->xTab
= lpitem
->rect
.right
;
1029 lpitem
->rect
.right
+= arrow_bitmap_width
;
1030 } else if (lpitem
->hbmpItem
) { /* menuBar */
1033 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1034 lpitem
->bmpsize
= size
;
1035 lpitem
->rect
.right
+= size
.cx
;
1036 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1037 itemheight
= size
.cy
;
1040 /* it must be a text item - unless it's the system menu */
1041 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1042 HFONT hfontOld
= NULL
;
1043 RECT rc
= lpitem
->rect
;
1044 LONG txtheight
, txtwidth
;
1046 if ( lpitem
->fState
& MFS_DEFAULT
) {
1047 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1050 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1051 DT_SINGLELINE
|DT_CALCRECT
);
1052 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1053 itemheight
= max( max( itemheight
, txtheight
),
1054 GetSystemMetrics( SM_CYMENU
) - 1);
1055 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1057 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1060 int n
= (int)( p
- lpitem
->text
);
1061 /* Item contains a tab (only meaningful in popup menus) */
1062 /* get text size before the tab */
1063 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1064 DT_SINGLELINE
|DT_CALCRECT
);
1065 txtwidth
= rc
.right
- rc
.left
;
1066 p
+= 1; /* advance past the Tab */
1067 /* get text size after the tab */
1068 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1069 DT_SINGLELINE
|DT_CALCRECT
);
1070 lpitem
->xTab
+= txtwidth
;
1071 txtheight
= max( txtheight
, tmpheight
);
1072 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1073 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1075 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1076 DT_SINGLELINE
|DT_CALCRECT
);
1077 txtwidth
= rc
.right
- rc
.left
;
1078 lpitem
->xTab
+= txtwidth
;
1080 lpitem
->rect
.right
+= 2 + txtwidth
;
1081 itemheight
= max( itemheight
,
1082 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1084 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1085 } else if( menuBar
) {
1086 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1088 lpitem
->rect
.bottom
+= itemheight
;
1089 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1093 /***********************************************************************
1094 * MENU_GetMaxPopupHeight
1097 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1100 return lppop
->cyMax
;
1101 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1105 /***********************************************************************
1106 * MENU_PopupMenuCalcSize
1108 * Calculate the size of a popup menu.
1110 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1115 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1117 lppop
->Width
= lppop
->Height
= 0;
1118 if (lppop
->nItems
== 0) return;
1121 SelectObject( hdc
, get_menu_font(FALSE
));
1126 lppop
->maxBmpSize
.cx
= 0;
1127 lppop
->maxBmpSize
.cy
= 0;
1129 while (start
< lppop
->nItems
)
1131 lpitem
= &lppop
->items
[start
];
1133 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1134 orgX
+= MENU_COL_SPACE
;
1135 orgY
= MENU_TOP_MARGIN
;
1137 maxTab
= maxTabWidth
= 0;
1138 /* Parse items until column break or end of menu */
1139 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1142 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1144 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1145 maxX
= max( maxX
, lpitem
->rect
.right
);
1146 orgY
= lpitem
->rect
.bottom
;
1147 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1149 maxTab
= max( maxTab
, lpitem
->xTab
);
1150 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1154 /* Finish the column (set all items to the largest width found) */
1155 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1156 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1158 lpitem
->rect
.right
= maxX
;
1159 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1160 lpitem
->xTab
= maxTab
;
1163 lppop
->Height
= max( lppop
->Height
, orgY
);
1166 lppop
->Width
= maxX
;
1168 /* space for 3d border */
1169 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1172 /* Adjust popup height if it exceeds maximum */
1173 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1174 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1175 if (lppop
->Height
>= maxHeight
)
1177 lppop
->Height
= maxHeight
;
1178 lppop
->bScrolling
= TRUE
;
1182 lppop
->bScrolling
= FALSE
;
1185 ReleaseDC( 0, hdc
);
1189 /***********************************************************************
1190 * MENU_MenuBarCalcSize
1192 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1193 * height is off by 1 pixel which causes lengthy window relocations when
1194 * active document window is maximized/restored.
1196 * Calculate the size of the menu bar.
1198 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1199 LPPOPUPMENU lppop
, HWND hwndOwner
)
1202 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1204 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1205 if (lppop
->nItems
== 0) return;
1206 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1207 lppop
->Width
= lprect
->right
- lprect
->left
;
1209 maxY
= lprect
->top
+1;
1212 lppop
->maxBmpSize
.cx
= 0;
1213 lppop
->maxBmpSize
.cy
= 0;
1214 while (start
< lppop
->nItems
)
1216 lpitem
= &lppop
->items
[start
];
1217 orgX
= lprect
->left
;
1220 /* Parse items until line break or end of menu */
1221 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1223 if ((helpPos
== -1) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1225 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1227 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1228 debug_print_menuitem (" item: ", lpitem
, "");
1229 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1231 if (lpitem
->rect
.right
> lprect
->right
)
1233 if (i
!= start
) break;
1234 else lpitem
->rect
.right
= lprect
->right
;
1236 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1237 orgX
= lpitem
->rect
.right
;
1240 /* Finish the line (set all items to the largest height found) */
1241 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1244 lprect
->bottom
= maxY
;
1245 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1247 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1248 /* the last item (if several lines, only move the last line) */
1249 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1250 orgY
= lpitem
->rect
.top
;
1251 orgX
= lprect
->right
;
1252 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1253 if ( (helpPos
==-1) || (helpPos
>i
) )
1255 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1256 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1257 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1258 lpitem
->rect
.right
= orgX
;
1259 orgX
= lpitem
->rect
.left
;
1264 /***********************************************************************
1265 * MENU_DrawScrollArrows
1267 * Draw scroll arrows.
1270 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1272 HDC hdcMem
= CreateCompatibleDC(hdc
);
1273 HBITMAP hOrigBitmap
;
1274 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1278 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1279 arrow_bitmap_width
= bmp
.bmWidth
;
1280 arrow_bitmap_height
= bmp
.bmHeight
;
1283 if (lppop
->nScrollPos
)
1284 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1286 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1289 rect
.right
= lppop
->Width
;
1290 rect
.bottom
= arrow_bitmap_height
;
1291 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1292 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1293 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1294 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1295 rect
.bottom
= lppop
->Height
;
1296 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1297 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1298 SelectObject(hdcMem
, get_down_arrow_bitmap());
1300 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1301 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1302 lppop
->Height
- arrow_bitmap_height
,
1303 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1304 SelectObject(hdcMem
, hOrigBitmap
);
1309 /***********************************************************************
1312 * Draws the popup-menu arrow.
1314 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1315 UINT arrow_bitmap_height
)
1317 HDC hdcMem
= CreateCompatibleDC( hdc
);
1318 HBITMAP hOrigBitmap
;
1320 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1321 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1322 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1323 arrow_bitmap_width
, arrow_bitmap_height
,
1324 hdcMem
, 0, 0, SRCCOPY
);
1325 SelectObject( hdcMem
, hOrigBitmap
);
1328 /***********************************************************************
1331 * Draw a single menu item.
1333 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1334 UINT height
, BOOL menuBar
, UINT odaction
)
1337 BOOL flat_menu
= FALSE
;
1339 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1340 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1343 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1347 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1348 arrow_bitmap_width
= bmp
.bmWidth
;
1349 arrow_bitmap_height
= bmp
.bmHeight
;
1352 if (lpitem
->fType
& MF_SYSMENU
)
1354 if( !IsIconic(hwnd
) )
1355 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1359 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1360 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1364 if (lpitem
->fState
& MF_HILITE
)
1366 if(menuBar
&& !flat_menu
) {
1367 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1368 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1370 if(lpitem
->fState
& MF_GRAYED
)
1371 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1373 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1374 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1379 if (lpitem
->fState
& MF_GRAYED
)
1380 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1382 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1383 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1386 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1387 rect
= lpitem
->rect
;
1388 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1390 if (lpitem
->fType
& MF_OWNERDRAW
)
1393 ** Experimentation under Windows reveals that an owner-drawn
1394 ** menu is given the rectangle which includes the space it requested
1395 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1396 ** and a popup-menu arrow. This is the value of lpitem->rect.
1397 ** Windows will leave all drawing to the application except for
1398 ** the popup-menu arrow. Windows always draws that itself, after
1399 ** the menu owner has finished drawing.
1403 dis
.CtlType
= ODT_MENU
;
1405 dis
.itemID
= lpitem
->wID
;
1406 dis
.itemData
= lpitem
->dwItemData
;
1408 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1409 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1410 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1411 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1412 dis
.hwndItem
= (HWND
)hmenu
;
1415 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1416 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1417 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1418 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1419 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1420 /* Draw the popup-menu arrow */
1421 if (lpitem
->fType
& MF_POPUP
)
1422 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1423 arrow_bitmap_height
);
1427 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1429 if (lpitem
->fState
& MF_HILITE
)
1433 InflateRect (&rect
, -1, -1);
1434 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1435 InflateRect (&rect
, 1, 1);
1436 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1441 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1443 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1447 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1449 SetBkMode( hdc
, TRANSPARENT
);
1451 /* vertical separator */
1452 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1457 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1459 rc
.bottom
= height
- 3;
1462 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1463 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1464 LineTo( hdc
, rc
.left
, rc
.bottom
);
1465 SelectObject( hdc
, oldPen
);
1468 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1471 /* horizontal separator */
1472 if (lpitem
->fType
& MF_SEPARATOR
)
1479 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1482 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1483 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1484 LineTo( hdc
, rc
.right
, rc
.top
);
1485 SelectObject( hdc
, oldPen
);
1488 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1492 /* helper lines for debugging */
1493 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1494 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1495 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1496 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1499 if (lpitem
->hbmpItem
) {
1500 /* calculate the bitmap rectangle in coordinates relative
1501 * to the item rectangle */
1503 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1506 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1508 else if (menu
->dwStyle
& MNS_NOCHECK
)
1510 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1513 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1514 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1515 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1518 bmprc
.top
= (rect
.bottom
- rect
.top
-
1519 lpitem
->bmpsize
.cy
) / 2;
1520 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1526 INT y
= rect
.top
+ rect
.bottom
;
1528 int checked
= FALSE
;
1529 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1530 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1531 /* Draw the check mark
1534 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1536 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1537 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1538 lpitem
->hUnCheckBit
;
1539 if (bm
) /* we have a custom bitmap */
1541 HDC hdcMem
= CreateCompatibleDC( hdc
);
1543 SelectObject( hdcMem
, bm
);
1544 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1545 check_bitmap_width
, check_bitmap_height
,
1546 hdcMem
, 0, 0, SRCCOPY
);
1550 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1553 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1554 check_bitmap_height
, 1, 1, NULL
);
1555 HDC hdcMem
= CreateCompatibleDC( hdc
);
1557 SelectObject( hdcMem
, bm
);
1558 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1559 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1560 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1561 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1562 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1563 hdcMem
, 0, 0, SRCCOPY
);
1569 if( lpitem
->hbmpItem
&&
1570 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1572 /* some applications make this assumption on the DC's origin */
1573 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1574 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1576 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1578 /* Draw the popup-menu arrow */
1579 if (lpitem
->fType
& MF_POPUP
)
1580 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1581 arrow_bitmap_height
);
1583 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1584 rect
.left
+= check_bitmap_width
;
1585 rect
.right
-= arrow_bitmap_width
;
1587 else if( lpitem
->hbmpItem
)
1588 { /* Draw the bitmap */
1591 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1592 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1594 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1596 /* process text if present */
1602 UINT uFormat
= (menuBar
) ?
1603 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1604 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1606 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1607 rect
.left
+= menu
->maxBmpSize
.cx
;
1609 if ( lpitem
->fState
& MFS_DEFAULT
)
1611 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1615 if( lpitem
->hbmpItem
)
1616 rect
.left
+= lpitem
->bmpsize
.cx
;
1617 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1618 rect
.left
+= menucharsize
.cx
;
1619 rect
.right
-= menucharsize
.cx
;
1622 for (i
= 0; lpitem
->text
[i
]; i
++)
1623 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1626 if(lpitem
->fState
& MF_GRAYED
)
1628 if (!(lpitem
->fState
& MF_HILITE
) )
1630 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1631 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1632 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1633 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1635 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1638 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1640 /* paint the shortcut text */
1641 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1643 if (lpitem
->text
[i
] == '\t')
1645 rect
.left
= lpitem
->xTab
;
1646 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1650 rect
.right
= lpitem
->xTab
;
1651 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1654 if(lpitem
->fState
& MF_GRAYED
)
1656 if (!(lpitem
->fState
& MF_HILITE
) )
1658 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1659 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1660 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1661 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1663 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1665 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1669 SelectObject (hdc
, hfontOld
);
1674 /***********************************************************************
1675 * MENU_DrawPopupMenu
1677 * Paint a popup menu.
1679 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1681 HBRUSH hPrevBrush
= 0;
1684 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1686 GetClientRect( hwnd
, &rect
);
1688 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1689 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1693 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1695 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1699 BOOL flat_menu
= FALSE
;
1701 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1703 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1705 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1707 if( (menu
= MENU_GetMenu( hmenu
)))
1709 /* draw menu items */
1716 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1717 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1718 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1720 /* draw scroll arrows */
1721 if (menu
->bScrolling
)
1722 MENU_DrawScrollArrows(menu
, hdc
);
1726 SelectObject( hdc
, hPrevBrush
);
1731 /***********************************************************************
1734 * Paint a menu bar. Returns the height of the menu bar.
1735 * called from [windows/nonclient.c]
1737 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1742 HMENU hMenu
= GetMenu(hwnd
);
1744 lppop
= MENU_GetMenu( hMenu
);
1745 if (lppop
== NULL
|| lprect
== NULL
)
1747 return GetSystemMetrics(SM_CYMENU
);
1752 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1754 if (lppop
->Height
== 0)
1755 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1757 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1759 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1760 return lppop
->Height
;
1763 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1767 /***********************************************************************
1770 * Display a popup menu.
1772 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1773 INT x
, INT y
, INT xanchor
, INT yanchor
)
1781 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1782 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1784 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1785 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1787 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1788 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1791 /* store the owner for DrawItem */
1792 menu
->hwndOwner
= hwndOwner
;
1794 menu
->nScrollPos
= 0;
1795 MENU_PopupMenuCalcSize( menu
);
1797 /* adjust popup menu pos so that it fits within the desktop */
1799 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1800 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1802 /* FIXME: should use item rect */
1805 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1806 info
.cbSize
= sizeof(info
);
1807 GetMonitorInfoW( monitor
, &info
);
1809 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1810 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1812 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1813 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1815 if( x
+ width
> info
.rcWork
.right
)
1817 if( xanchor
&& x
>= width
- xanchor
)
1818 x
-= width
- xanchor
;
1820 if( x
+ width
> info
.rcWork
.right
)
1821 x
= info
.rcWork
.right
- width
;
1823 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1825 if( y
+ height
> info
.rcWork
.bottom
)
1827 if( yanchor
&& y
>= height
+ yanchor
)
1828 y
-= height
+ yanchor
;
1830 if( y
+ height
> info
.rcWork
.bottom
)
1831 y
= info
.rcWork
.bottom
- height
;
1833 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1835 /* NOTE: In Windows, top menu popup is not owned. */
1836 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1837 WS_POPUP
, x
, y
, width
, height
,
1838 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1840 if( !menu
->hWnd
) return FALSE
;
1841 if (!top_popup
) top_popup
= menu
->hWnd
;
1843 /* Display the window */
1845 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1846 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1847 UpdateWindow( menu
->hWnd
);
1852 /***********************************************************************
1853 * MENU_EnsureMenuItemVisible
1856 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1858 if (lppop
->bScrolling
)
1860 MENUITEM
*item
= &lppop
->items
[wIndex
];
1861 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1862 UINT nOldPos
= lppop
->nScrollPos
;
1864 UINT arrow_bitmap_height
;
1867 GetClientRect(lppop
->hWnd
, &rc
);
1869 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1870 arrow_bitmap_height
= bmp
.bmHeight
;
1872 rc
.top
+= arrow_bitmap_height
;
1873 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1875 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1876 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1879 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1880 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1881 MENU_DrawScrollArrows(lppop
, hdc
);
1883 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1885 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1886 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1887 MENU_DrawScrollArrows(lppop
, hdc
);
1893 /***********************************************************************
1896 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1897 BOOL sendMenuSelect
, HMENU topmenu
)
1902 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1904 lppop
= MENU_GetMenu( hmenu
);
1905 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1907 if (lppop
->FocusedItem
== wIndex
) return;
1908 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1909 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1910 if (!top_popup
) top_popup
= lppop
->hWnd
;
1912 SelectObject( hdc
, get_menu_font(FALSE
));
1914 /* Clear previous highlighted item */
1915 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1917 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1918 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1919 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1923 /* Highlight new item (if any) */
1924 lppop
->FocusedItem
= wIndex
;
1925 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1927 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1928 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1929 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1930 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1931 &lppop
->items
[wIndex
], lppop
->Height
,
1932 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1936 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1937 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1938 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1939 ip
->fType
| ip
->fState
|
1940 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1943 else if (sendMenuSelect
) {
1946 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1947 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1948 MENUITEM
*ip
= &ptm
->items
[pos
];
1949 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1950 ip
->fType
| ip
->fState
|
1951 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1955 ReleaseDC( lppop
->hWnd
, hdc
);
1959 /***********************************************************************
1960 * MENU_MoveSelection
1962 * Moves currently selected item according to the offset parameter.
1963 * If there is no selection then it should select the last item if
1964 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1966 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1971 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1973 menu
= MENU_GetMenu( hmenu
);
1974 if ((!menu
) || (!menu
->items
)) return;
1976 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1978 if( menu
->nItems
== 1 ) return; else
1979 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1981 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1983 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1988 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1989 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1990 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1992 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1998 /**********************************************************************
2001 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2004 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
2007 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
2008 TRACE("flags=%x str=%p\n", flags
, str
);
2010 if (IS_STRING_ITEM(flags
))
2012 LPWSTR prevText
= item
->text
;
2015 flags
|= MF_SEPARATOR
;
2021 /* Item beginning with a backspace is a help item */
2027 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
2029 strcpyW( text
, str
);
2032 item
->hbmpItem
= NULL
;
2033 HeapFree( GetProcessHeap(), 0, prevText
);
2035 else if(( flags
& MFT_BITMAP
)) {
2036 item
->hbmpItem
= HBITMAP_32(LOWORD(str
));
2037 /* setting bitmap clears text */
2038 HeapFree( GetProcessHeap(), 0, item
->text
);
2042 if (flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
2044 if (flags
& MF_OWNERDRAW
)
2045 item
->dwItemData
= (DWORD_PTR
)str
;
2047 item
->dwItemData
= 0;
2049 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
2050 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
2052 if (flags
& MF_POPUP
)
2054 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
2055 if (menu
) menu
->wFlags
|= MF_POPUP
;
2067 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
2069 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
2070 flags
|= MF_POPUP
; /* keep popup */
2072 item
->fType
= flags
& TYPE_MASK
;
2073 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2074 for ModifyMenu, but Windows accepts it */
2075 item
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
2077 /* Don't call SetRectEmpty here! */
2079 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
2084 /**********************************************************************
2087 * Insert (allocate) a new item into a menu.
2089 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2094 if (!(menu
= MENU_GetMenu(hMenu
)))
2097 /* Find where to insert new item */
2099 if (flags
& MF_BYPOSITION
) {
2100 if (pos
> menu
->nItems
)
2103 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2106 if (!(menu
= MENU_GetMenu( hMenu
)))
2111 /* Make sure that MDI system buttons stay on the right side.
2112 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2113 * regardless of their id.
2115 while (pos
> 0 && (menu
->items
[pos
- 1].fType
& MFT_BITMAP
) &&
2116 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2117 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2120 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2122 /* Create new items array */
2124 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2127 WARN("allocation failed\n" );
2130 if (menu
->nItems
> 0)
2132 /* Copy the old array into the new one */
2133 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2134 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2135 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2136 HeapFree( GetProcessHeap(), 0, menu
->items
);
2138 menu
->items
= newItems
;
2140 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2141 menu
->Height
= 0; /* force size recalculate */
2142 return &newItems
[pos
];
2146 /**********************************************************************
2147 * MENU_ParseResource
2149 * Parse a standard menu resource and add items to the menu.
2150 * Return a pointer to the end of the resource.
2152 * NOTE: flags is equivalent to the mtOption field
2154 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2162 flags
= GET_WORD(res
);
2163 end_flag
= flags
& MF_END
;
2164 /* Remove MF_END because it has the same value as MF_HILITE */
2166 res
+= sizeof(WORD
);
2167 if (!(flags
& MF_POPUP
))
2170 res
+= sizeof(WORD
);
2173 if (!unicode
) res
+= strlen(str
) + 1;
2174 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2175 if (flags
& MF_POPUP
)
2177 HMENU hSubMenu
= CreatePopupMenu();
2178 if (!hSubMenu
) return NULL
;
2179 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2181 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2182 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2184 else /* Not a popup */
2186 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2187 else AppendMenuW( hMenu
, flags
, id
,
2188 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2190 } while (!end_flag
);
2195 /**********************************************************************
2196 * MENUEX_ParseResource
2198 * Parse an extended menu resource and add items to the menu.
2199 * Return a pointer to the end of the resource.
2201 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2207 mii
.cbSize
= sizeof(mii
);
2208 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2209 mii
.fType
= GET_DWORD(res
);
2210 res
+= sizeof(DWORD
);
2211 mii
.fState
= GET_DWORD(res
);
2212 res
+= sizeof(DWORD
);
2213 mii
.wID
= GET_DWORD(res
);
2214 res
+= sizeof(DWORD
);
2215 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2216 res
+= sizeof(WORD
);
2217 /* Align the text on a word boundary. */
2218 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2219 mii
.dwTypeData
= (LPWSTR
) res
;
2220 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2221 /* Align the following fields on a dword boundary. */
2222 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2224 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2225 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2227 if (resinfo
& 1) { /* Pop-up? */
2228 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2229 res
+= sizeof(DWORD
);
2230 mii
.hSubMenu
= CreatePopupMenu();
2233 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2234 DestroyMenu(mii
.hSubMenu
);
2237 mii
.fMask
|= MIIM_SUBMENU
;
2238 mii
.fType
|= MF_POPUP
;
2240 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2242 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2243 mii
.wID
, mii
.fType
);
2244 mii
.fType
|= MF_SEPARATOR
;
2246 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2247 } while (!(resinfo
& MF_END
));
2252 /***********************************************************************
2255 * Return the handle of the selected sub-popup menu (if any).
2257 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2262 menu
= MENU_GetMenu( hmenu
);
2264 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2266 item
= &menu
->items
[menu
->FocusedItem
];
2267 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2268 return item
->hSubMenu
;
2273 /***********************************************************************
2274 * MENU_HideSubPopups
2276 * Hide the sub-popup menus of this menu.
2278 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2279 BOOL sendMenuSelect
, UINT wFlags
)
2281 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2283 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2285 if (menu
&& top_popup
)
2291 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2293 item
= &menu
->items
[menu
->FocusedItem
];
2294 if (!(item
->fType
& MF_POPUP
) ||
2295 !(item
->fState
& MF_MOUSESELECT
)) return;
2296 item
->fState
&= ~MF_MOUSESELECT
;
2297 hsubmenu
= item
->hSubMenu
;
2300 submenu
= MENU_GetMenu( hsubmenu
);
2301 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2302 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2303 DestroyWindow( submenu
->hWnd
);
2306 if (!(wFlags
& TPM_NONOTIFY
))
2307 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2308 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2313 /***********************************************************************
2316 * Display the sub-menu of the selected item of this menu.
2317 * Return the handle of the submenu, or hmenu if no submenu to display.
2319 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2320 BOOL selectFirst
, UINT wFlags
)
2327 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2329 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2331 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2333 item
= &menu
->items
[menu
->FocusedItem
];
2334 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2337 /* message must be sent before using item,
2338 because nearly everything may be changed by the application ! */
2340 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2341 if (!(wFlags
& TPM_NONOTIFY
))
2342 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2343 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2345 item
= &menu
->items
[menu
->FocusedItem
];
2348 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2349 if (!(item
->fState
& MF_HILITE
))
2351 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2352 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2354 SelectObject( hdc
, get_menu_font(FALSE
));
2356 item
->fState
|= MF_HILITE
;
2357 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2358 ReleaseDC( menu
->hWnd
, hdc
);
2360 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2363 item
->fState
|= MF_MOUSESELECT
;
2365 if (IS_SYSTEM_MENU(menu
))
2367 MENU_InitSysMenuPopup(item
->hSubMenu
,
2368 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2369 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2371 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2372 rect
.top
= rect
.bottom
;
2373 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2374 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2378 GetWindowRect( menu
->hWnd
, &rect
);
2379 if (menu
->wFlags
& MF_POPUP
)
2381 RECT rc
= item
->rect
;
2383 MENU_AdjustMenuItemRect(menu
, &rc
);
2385 /* The first item in the popup menu has to be at the
2386 same y position as the focused menu item */
2387 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2388 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2389 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2390 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2391 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2395 rect
.left
+= item
->rect
.left
;
2396 rect
.top
+= item
->rect
.bottom
;
2397 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2398 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2402 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2403 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2405 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2406 return item
->hSubMenu
;
2411 /**********************************************************************
2414 HWND
MENU_IsMenuActive(void)
2419 /***********************************************************************
2422 * Walks menu chain trying to find a menu pt maps to.
2424 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2426 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2427 UINT item
= menu
->FocusedItem
;
2430 /* try subpopup first (if any) */
2431 ret
= (item
!= NO_SELECTED_ITEM
&&
2432 (menu
->items
[item
].fType
& MF_POPUP
) &&
2433 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2434 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2436 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2438 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2439 if( menu
->wFlags
& MF_POPUP
)
2441 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2443 else if (ht
== HTSYSMENU
)
2444 ret
= get_win_sys_menu( menu
->hWnd
);
2445 else if (ht
== HTMENU
)
2446 ret
= GetMenu( menu
->hWnd
);
2451 /***********************************************************************
2452 * MENU_ExecFocusedItem
2454 * Execute a menu item (for instance when user pressed Enter).
2455 * Return the wID of the executed item. Otherwise, -1 indicating
2456 * that no menu item was executed, -2 if a popup is shown;
2457 * Have to receive the flags for the TrackPopupMenu options to avoid
2458 * sending unwanted message.
2461 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2464 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2466 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2468 if (!menu
|| !menu
->nItems
||
2469 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2471 item
= &menu
->items
[menu
->FocusedItem
];
2473 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2475 if (!(item
->fType
& MF_POPUP
))
2477 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2479 /* If TPM_RETURNCMD is set you return the id, but
2480 do not send a message to the owner */
2481 if(!(wFlags
& TPM_RETURNCMD
))
2483 if( menu
->wFlags
& MF_SYSMENU
)
2484 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2485 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2488 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2489 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2491 if (dwStyle
& MNS_NOTIFYBYPOS
)
2492 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2495 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2503 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2510 /***********************************************************************
2511 * MENU_SwitchTracking
2513 * Helper function for menu navigation routines.
2515 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2517 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2518 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2520 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2522 if( pmt
->hTopMenu
!= hPtMenu
&&
2523 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2525 /* both are top level menus (system and menu-bar) */
2526 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2527 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2528 pmt
->hTopMenu
= hPtMenu
;
2530 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2531 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2535 /***********************************************************************
2538 * Return TRUE if we can go on with menu tracking.
2540 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2542 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2547 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2550 if( IS_SYSTEM_MENU(ptmenu
) )
2551 item
= ptmenu
->items
;
2553 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2557 if( ptmenu
->FocusedItem
!= id
)
2558 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2560 /* If the popup menu is not already "popped" */
2561 if(!(item
->fState
& MF_MOUSESELECT
))
2563 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2568 /* Else the click was on the menu bar, finish the tracking */
2573 /***********************************************************************
2576 * Return the value of MENU_ExecFocusedItem if
2577 * the selected item was not a popup. Else open the popup.
2578 * A -1 return value indicates that we go on with menu tracking.
2581 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2583 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2588 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2591 if( IS_SYSTEM_MENU(ptmenu
) )
2592 item
= ptmenu
->items
;
2594 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2596 if( item
&& (ptmenu
->FocusedItem
== id
))
2598 debug_print_menuitem ("FocusedItem: ", item
, "");
2600 if( !(item
->fType
& MF_POPUP
) )
2602 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2603 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2604 return executedMenuId
;
2607 /* If we are dealing with the top-level menu */
2608 /* and this is a click on an already "popped" item: */
2609 /* Stop the menu tracking and close the opened submenus */
2610 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2613 ptmenu
->bTimeToHide
= TRUE
;
2619 /***********************************************************************
2622 * Return TRUE if we can go on with menu tracking.
2624 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2626 UINT id
= NO_SELECTED_ITEM
;
2627 POPUPMENU
*ptmenu
= NULL
;
2631 ptmenu
= MENU_GetMenu( hPtMenu
);
2632 if( IS_SYSTEM_MENU(ptmenu
) )
2635 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2638 if( id
== NO_SELECTED_ITEM
)
2640 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2641 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2644 else if( ptmenu
->FocusedItem
!= id
)
2646 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2647 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2653 /***********************************************************************
2656 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2658 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2660 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2663 /* When skipping left, we need to do something special after the
2665 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2669 /* When skipping right, for the non-system menu, we need to
2670 handle the last non-special menu item (ie skip any window
2671 icons such as MDI maximize, restore or close) */
2672 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2674 int i
= menu
->FocusedItem
+ 1;
2675 while (i
< menu
->nItems
) {
2676 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2677 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2681 if (i
== menu
->nItems
) {
2685 /* When skipping right, we need to cater for the system menu */
2686 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2688 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2695 MDINEXTMENU next_menu
;
2700 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2701 next_menu
.hmenuNext
= 0;
2702 next_menu
.hwndNext
= 0;
2703 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2705 TRACE("%p [%p] -> %p [%p]\n",
2706 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2708 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2710 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2711 hNewWnd
= pmt
->hOwnerWnd
;
2712 if( IS_SYSTEM_MENU(menu
) )
2714 /* switch to the menu bar */
2716 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2720 menu
= MENU_GetMenu( hNewMenu
);
2721 id
= menu
->nItems
- 1;
2723 /* Skip backwards over any system predefined icons,
2724 eg. MDI close, restore etc icons */
2726 (menu
->items
[id
].wID
>= SC_SIZE
&&
2727 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2730 else if (style
& WS_SYSMENU
)
2732 /* switch to the system menu */
2733 hNewMenu
= get_win_sys_menu( hNewWnd
);
2737 else /* application returned a new menu to switch to */
2739 hNewMenu
= next_menu
.hmenuNext
;
2740 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2742 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2744 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2746 if (style
& WS_SYSMENU
&&
2747 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2749 /* get the real system menu */
2750 hNewMenu
= get_win_sys_menu(hNewWnd
);
2752 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2754 /* FIXME: Not sure what to do here;
2755 * perhaps try to track hNewMenu as a popup? */
2757 TRACE(" -- got confused.\n");
2764 if( hNewMenu
!= pmt
->hTopMenu
)
2766 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2768 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2769 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2772 if( hNewWnd
!= pmt
->hOwnerWnd
)
2774 pmt
->hOwnerWnd
= hNewWnd
;
2775 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2778 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2779 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2786 /***********************************************************************
2789 * The idea is not to show the popup if the next input message is
2790 * going to hide it anyway.
2792 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2796 msg
.hwnd
= pmt
->hOwnerWnd
;
2798 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2799 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2804 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2805 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2807 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2808 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2809 if( msg
.message
== WM_KEYDOWN
&&
2810 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2812 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2819 /* failures go through this */
2820 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2824 /***********************************************************************
2827 * Handle a VK_ESCAPE key event in a menu.
2829 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2831 BOOL bEndMenu
= TRUE
;
2833 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2835 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2837 if (menu
->wFlags
& MF_POPUP
)
2839 HMENU hmenutmp
, hmenuprev
;
2841 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2843 /* close topmost popup */
2844 while (hmenutmp
!= pmt
->hCurrentMenu
)
2846 hmenuprev
= hmenutmp
;
2847 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2850 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2851 pmt
->hCurrentMenu
= hmenuprev
;
2859 /***********************************************************************
2862 * Handle a VK_LEFT key event in a menu.
2864 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2867 HMENU hmenutmp
, hmenuprev
;
2870 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2871 menu
= MENU_GetMenu( hmenutmp
);
2873 /* Try to move 1 column left (if possible) */
2874 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2875 NO_SELECTED_ITEM
) {
2877 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2882 /* close topmost popup */
2883 while (hmenutmp
!= pmt
->hCurrentMenu
)
2885 hmenuprev
= hmenutmp
;
2886 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2889 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2890 pmt
->hCurrentMenu
= hmenuprev
;
2892 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2894 /* move menu bar selection if no more popups are left */
2896 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2897 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2899 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2901 /* A sublevel menu was displayed - display the next one
2902 * unless there is another displacement coming up */
2904 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2905 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2906 pmt
->hTopMenu
, TRUE
, wFlags
);
2912 /***********************************************************************
2915 * Handle a VK_RIGHT key event in a menu.
2917 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2920 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2923 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2925 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2926 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2928 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2930 /* If already displaying a popup, try to display sub-popup */
2932 hmenutmp
= pmt
->hCurrentMenu
;
2933 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2935 /* if subpopup was displayed then we are done */
2936 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2939 /* Check to see if there's another column */
2940 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2941 NO_SELECTED_ITEM
) {
2942 TRACE("Going to %d.\n", nextcol
);
2943 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2948 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2950 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2952 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2953 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2954 } else hmenutmp
= 0;
2956 /* try to move to the next item */
2957 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2958 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2960 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2961 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2962 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2963 pmt
->hTopMenu
, TRUE
, wFlags
);
2967 /***********************************************************************
2970 * Menu tracking code.
2972 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2973 HWND hwnd
, const RECT
*lprect
)
2978 INT executedMenuId
= -1;
2980 BOOL enterIdleSent
= FALSE
;
2984 mt
.hCurrentMenu
= hmenu
;
2985 mt
.hTopMenu
= hmenu
;
2986 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2990 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2991 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2994 if (!(menu
= MENU_GetMenu( hmenu
)))
2996 WARN("Invalid menu handle %p\n", hmenu
);
2997 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3001 if (wFlags
& TPM_BUTTONDOWN
)
3003 /* Get the result in order to start the tracking or not */
3004 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3005 fEndMenu
= !fRemove
;
3008 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3010 /* owner may not be visible when tracking a popup, so use the menu itself */
3011 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3012 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3016 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3017 if (!menu
) /* sometimes happens if I do a window manager close */
3020 /* we have to keep the message in the queue until it's
3021 * clear that menu loop is not over yet. */
3025 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3027 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3028 /* remove the message from the queue */
3029 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3035 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
3036 enterIdleSent
= TRUE
;
3037 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3043 /* check if EndMenu() tried to cancel us, by posting this message */
3044 if(msg
.message
== WM_CANCELMODE
)
3046 /* we are now out of the loop */
3049 /* remove the message from the queue */
3050 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3052 /* break out of internal loop, ala ESCAPE */
3056 TranslateMessage( &msg
);
3059 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3060 enterIdleSent
=FALSE
;
3063 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3066 * Use the mouse coordinates in lParam instead of those in the MSG
3067 * struct to properly handle synthetic messages. They are already
3068 * in screen coordinates.
3070 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3071 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3073 /* Find a menu for this mouse event */
3074 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3078 /* no WM_NC... messages in captured state */
3080 case WM_RBUTTONDBLCLK
:
3081 case WM_RBUTTONDOWN
:
3082 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3084 case WM_LBUTTONDBLCLK
:
3085 case WM_LBUTTONDOWN
:
3086 /* If the message belongs to the menu, removes it from the queue */
3087 /* Else, end menu tracking */
3088 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3089 fEndMenu
= !fRemove
;
3093 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3096 /* Check if a menu was selected by the mouse */
3099 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3100 TRACE("executedMenuId %d\n", executedMenuId
);
3102 /* End the loop if executedMenuId is an item ID */
3103 /* or if the job was done (executedMenuId = 0). */
3104 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3106 /* No menu was selected by the mouse */
3107 /* if the function was called by TrackPopupMenu, continue
3108 with the menu tracking. If not, stop it */
3110 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3115 /* the selected menu item must be changed every time */
3116 /* the mouse moves. */
3119 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3121 } /* switch(msg.message) - mouse */
3123 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3125 fRemove
= TRUE
; /* Keyboard messages are always removed */
3138 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3139 NO_SELECTED_ITEM
, FALSE
, 0 );
3140 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3141 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3145 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3147 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3148 if (!(menu
->wFlags
& MF_POPUP
))
3149 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3150 else /* otherwise try to move selection */
3151 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3152 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3156 MENU_KeyLeft( &mt
, wFlags
);
3160 MENU_KeyRight( &mt
, wFlags
);
3164 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3170 hi
.cbSize
= sizeof(HELPINFO
);
3171 hi
.iContextType
= HELPINFO_MENUITEM
;
3172 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3175 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3176 hi
.hItemHandle
= hmenu
;
3177 hi
.dwContextId
= menu
->dwContextHelpID
;
3178 hi
.MousePos
= msg
.pt
;
3179 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3186 break; /* WM_KEYDOWN */
3193 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3195 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3196 fEndMenu
= (executedMenuId
!= -2);
3201 /* Hack to avoid control chars. */
3202 /* We will find a better way real soon... */
3203 if (msg
.wParam
< 32) break;
3205 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3206 LOWORD(msg
.wParam
), FALSE
);
3207 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3208 else if (pos
== (UINT
)-1) MessageBeep(0);
3211 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3213 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3214 fEndMenu
= (executedMenuId
!= -2);
3218 } /* switch(msg.message) - kbd */
3222 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3223 DispatchMessageW( &msg
);
3227 if (!fEndMenu
) fRemove
= TRUE
;
3229 /* finally remove message from the queue */
3231 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3232 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3233 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3236 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3238 /* If dropdown is still painted and the close box is clicked on
3239 then the menu will be destroyed as part of the DispatchMessage above.
3240 This will then invalidate the menu handle in mt.hTopMenu. We should
3241 check for this first. */
3242 if( IsMenu( mt
.hTopMenu
) )
3244 menu
= MENU_GetMenu( mt
.hTopMenu
);
3246 if( IsWindow( mt
.hOwnerWnd
) )
3248 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3250 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3252 DestroyWindow( menu
->hWnd
);
3255 if (!(wFlags
& TPM_NONOTIFY
))
3256 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3257 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3259 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3260 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
3263 /* Reset the variable for hiding menu */
3264 if( menu
) menu
->bTimeToHide
= FALSE
;
3267 /* The return value is only used by TrackPopupMenu */
3268 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3269 if (executedMenuId
== -1) executedMenuId
= 0;
3270 return executedMenuId
;
3273 /***********************************************************************
3276 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3280 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3284 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3285 if (!(wFlags
& TPM_NONOTIFY
))
3286 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3288 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3290 if (!(wFlags
& TPM_NONOTIFY
))
3292 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3293 /* If an app changed/recreated menu bar entries in WM_INITMENU
3294 * menu sizes will be recalculated once the menu created/shown.
3298 /* This makes the menus of applications built with Delphi work.
3299 * It also enables menus to be displayed in more than one window,
3300 * but there are some bugs left that need to be fixed in this case.
3302 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3306 /***********************************************************************
3309 static BOOL
MENU_ExitTracking(HWND hWnd
)
3311 TRACE("hwnd=%p\n", hWnd
);
3313 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3319 /***********************************************************************
3320 * MENU_TrackMouseMenuBar
3322 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3324 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3326 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3327 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3329 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3333 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3334 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3335 MENU_ExitTracking(hWnd
);
3340 /***********************************************************************
3341 * MENU_TrackKbdMenuBar
3343 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3345 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3347 UINT uItem
= NO_SELECTED_ITEM
;
3349 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3351 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3353 /* find window that has a menu */
3355 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3356 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3358 /* check if we have to track a system menu */
3360 hTrackMenu
= GetMenu( hwnd
);
3361 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3363 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3364 hTrackMenu
= get_win_sys_menu( hwnd
);
3366 wParam
|= HTSYSMENU
; /* prevent item lookup */
3369 if (!IsMenu( hTrackMenu
)) return;
3371 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3373 if( wChar
&& wChar
!= ' ' )
3375 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3376 if ( uItem
>= (UINT
)(-2) )
3378 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3379 /* schedule end of menu tracking */
3380 wFlags
|= TF_ENDMENU
;
3385 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3387 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3389 if( uItem
== NO_SELECTED_ITEM
)
3390 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3392 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3396 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3397 MENU_ExitTracking( hwnd
);
3401 /**********************************************************************
3402 * TrackPopupMenu (USER32.@)
3404 * Like the win32 API, the function return the command ID only if the
3405 * flag TPM_RETURNCMD is on.
3408 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3409 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3413 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3414 hMenu
, wFlags
, x
, y
, nReserved
, hWnd
, wine_dbgstr_rect(lpRect
));
3416 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3418 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3419 if (!(wFlags
& TPM_NONOTIFY
))
3420 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3422 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3423 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3424 MENU_ExitTracking(hWnd
);
3429 /**********************************************************************
3430 * TrackPopupMenuEx (USER32.@)
3432 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3433 HWND hWnd
, LPTPMPARAMS lpTpm
)
3435 FIXME("not fully implemented\n" );
3436 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3437 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3440 /***********************************************************************
3443 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3445 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3447 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3453 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3454 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3458 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3459 return MA_NOACTIVATE
;
3464 BeginPaint( hwnd
, &ps
);
3465 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3466 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3467 EndPaint( hwnd
, &ps
);
3474 /* zero out global pointer in case resident popup window was destroyed. */
3475 if (hwnd
== top_popup
) top_popup
= 0;
3482 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3485 SetWindowLongPtrW( hwnd
, 0, 0 );
3488 case MM_SETMENUHANDLE
:
3489 SetWindowLongPtrW( hwnd
, 0, wParam
);
3492 case MM_GETMENUHANDLE
:
3493 return GetWindowLongPtrW( hwnd
, 0 );
3496 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3502 /***********************************************************************
3503 * MENU_GetMenuBarHeight
3505 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3507 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3508 INT orgX
, INT orgY
)
3514 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3516 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3518 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3519 SelectObject( hdc
, get_menu_font(FALSE
));
3520 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3521 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3522 ReleaseDC( hwnd
, hdc
);
3523 return lppop
->Height
;
3527 /*******************************************************************
3528 * ChangeMenuA (USER32.@)
3530 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3531 UINT id
, UINT flags
)
3533 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3534 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3536 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3537 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3539 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3540 flags
& MF_BYPOSITION
? pos
: id
,
3541 flags
& ~MF_REMOVE
);
3542 /* Default: MF_INSERT */
3543 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3547 /*******************************************************************
3548 * ChangeMenuW (USER32.@)
3550 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3551 UINT id
, UINT flags
)
3553 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3554 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3556 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3557 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3559 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3560 flags
& MF_BYPOSITION
? pos
: id
,
3561 flags
& ~MF_REMOVE
);
3562 /* Default: MF_INSERT */
3563 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3567 /*******************************************************************
3568 * CheckMenuItem (USER32.@)
3570 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3575 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3576 ret
= item
->fState
& MF_CHECKED
;
3577 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3578 else item
->fState
&= ~MF_CHECKED
;
3583 /**********************************************************************
3584 * EnableMenuItem (USER32.@)
3586 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3592 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3594 /* Get the Popupmenu to access the owner menu */
3595 if (!(menu
= MENU_GetMenu(hMenu
)))
3598 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3601 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3602 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3604 /* If the close item in the system menu change update the close button */
3605 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3607 if (menu
->hSysMenuOwner
!= 0)
3610 POPUPMENU
* parentMenu
;
3612 /* Get the parent menu to access*/
3613 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3616 /* Refresh the frame to reflect the change */
3617 GetWindowRect(parentMenu
->hWnd
, &rc
);
3618 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3620 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3628 /*******************************************************************
3629 * GetMenuStringA (USER32.@)
3631 INT WINAPI
GetMenuStringA(
3632 HMENU hMenu
, /* [in] menuhandle */
3633 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3634 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3635 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3636 UINT wFlags
/* [in] MF_ flags */
3640 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3641 if (str
&& nMaxSiz
) str
[0] = '\0';
3642 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3643 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3646 if (!item
->text
) return 0;
3647 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3648 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3650 TRACE("returning %s\n", debugstr_a(str
));
3655 /*******************************************************************
3656 * GetMenuStringW (USER32.@)
3658 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3659 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3663 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3664 if (str
&& nMaxSiz
) str
[0] = '\0';
3665 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3666 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3669 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3670 if( !(item
->text
)) {
3674 lstrcpynW( str
, item
->text
, nMaxSiz
);
3675 TRACE("returning %s\n", debugstr_w(str
));
3676 return strlenW(str
);
3680 /**********************************************************************
3681 * HiliteMenuItem (USER32.@)
3683 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3687 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3688 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3689 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3690 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3691 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3692 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3697 /**********************************************************************
3698 * GetMenuState (USER32.@)
3700 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3703 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3704 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3705 debug_print_menuitem (" item: ", item
, "");
3706 if (item
->fType
& MF_POPUP
)
3708 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3709 if (!menu
) return -1;
3710 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3714 /* We used to (from way back then) mask the result to 0xff. */
3715 /* I don't know why and it seems wrong as the documented */
3716 /* return flag MF_SEPARATOR is outside that mask. */
3717 return (item
->fType
| item
->fState
);
3722 /**********************************************************************
3723 * GetMenuItemCount (USER32.@)
3725 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3727 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3728 if (!menu
) return -1;
3729 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3730 return menu
->nItems
;
3734 /**********************************************************************
3735 * GetMenuItemID (USER32.@)
3737 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3741 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3742 if (lpmi
->fType
& MF_POPUP
) return -1;
3748 /*******************************************************************
3749 * InsertMenuW (USER32.@)
3751 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3752 UINT_PTR id
, LPCWSTR str
)
3756 if (IS_STRING_ITEM(flags
) && str
)
3757 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3758 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3759 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3760 hMenu
, pos
, flags
, id
, str
);
3762 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3764 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3766 RemoveMenu( hMenu
, pos
, flags
);
3770 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3775 /*******************************************************************
3776 * InsertMenuA (USER32.@)
3778 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3779 UINT_PTR id
, LPCSTR str
)
3783 if (IS_STRING_ITEM(flags
) && str
)
3785 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3786 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3789 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3790 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3791 HeapFree( GetProcessHeap(), 0, newstr
);
3795 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3799 /*******************************************************************
3800 * AppendMenuA (USER32.@)
3802 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3803 UINT_PTR id
, LPCSTR data
)
3805 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3809 /*******************************************************************
3810 * AppendMenuW (USER32.@)
3812 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3813 UINT_PTR id
, LPCWSTR data
)
3815 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3819 /**********************************************************************
3820 * RemoveMenu (USER32.@)
3822 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3827 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3828 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3829 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3833 MENU_FreeItemData( item
);
3835 if (--menu
->nItems
== 0)
3837 HeapFree( GetProcessHeap(), 0, menu
->items
);
3842 while(nPos
< menu
->nItems
)
3848 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3849 menu
->nItems
* sizeof(MENUITEM
) );
3855 /**********************************************************************
3856 * DeleteMenu (USER32.@)
3858 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3860 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3861 if (!item
) return FALSE
;
3862 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3863 /* nPos is now the position of the item */
3864 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3869 /*******************************************************************
3870 * ModifyMenuW (USER32.@)
3872 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3873 UINT_PTR id
, LPCWSTR str
)
3877 if (IS_STRING_ITEM(flags
))
3878 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3880 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3882 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3883 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3884 return MENU_SetItemData( item
, flags
, id
, str
);
3888 /*******************************************************************
3889 * ModifyMenuA (USER32.@)
3891 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3892 UINT_PTR id
, LPCSTR str
)
3896 if (IS_STRING_ITEM(flags
) && str
)
3898 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3899 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3902 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3903 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3904 HeapFree( GetProcessHeap(), 0, newstr
);
3908 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3912 /**********************************************************************
3913 * CreatePopupMenu (USER32.@)
3915 HMENU WINAPI
CreatePopupMenu(void)
3920 if (!(hmenu
= CreateMenu())) return 0;
3921 menu
= MENU_GetMenu( hmenu
);
3922 menu
->wFlags
|= MF_POPUP
;
3923 menu
->bTimeToHide
= FALSE
;
3928 /**********************************************************************
3929 * GetMenuCheckMarkDimensions (USER.417)
3930 * GetMenuCheckMarkDimensions (USER32.@)
3932 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3934 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3938 /**********************************************************************
3939 * SetMenuItemBitmaps (USER32.@)
3941 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3942 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3946 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3948 if (!hNewCheck
&& !hNewUnCheck
)
3950 item
->fState
&= ~MF_USECHECKBITMAPS
;
3952 else /* Install new bitmaps */
3954 item
->hCheckBit
= hNewCheck
;
3955 item
->hUnCheckBit
= hNewUnCheck
;
3956 item
->fState
|= MF_USECHECKBITMAPS
;
3962 /**********************************************************************
3963 * CreateMenu (USER32.@)
3965 HMENU WINAPI
CreateMenu(void)
3969 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3970 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3972 ZeroMemory(menu
, sizeof(POPUPMENU
));
3973 menu
->wMagic
= MENU_MAGIC
;
3974 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3975 menu
->bTimeToHide
= FALSE
;
3977 TRACE("return %p\n", hMenu
);
3983 /**********************************************************************
3984 * DestroyMenu (USER32.@)
3986 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3988 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3990 TRACE("(%p)\n", hMenu
);
3993 if (!lppop
) return FALSE
;
3995 lppop
->wMagic
= 0; /* Mark it as destroyed */
3997 /* DestroyMenu should not destroy system menu popup owner */
3998 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4000 DestroyWindow( lppop
->hWnd
);
4004 if (lppop
->items
) /* recursively destroy submenus */
4007 MENUITEM
*item
= lppop
->items
;
4008 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4010 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4011 MENU_FreeItemData( item
);
4013 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4015 USER_HEAP_FREE( hMenu
);
4020 /**********************************************************************
4021 * GetSystemMenu (USER32.@)
4023 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4025 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4028 if (wndPtr
== WND_DESKTOP
) return 0;
4029 if (wndPtr
== WND_OTHER_PROCESS
)
4031 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4035 if (wndPtr
->hSysMenu
&& bRevert
)
4037 DestroyMenu(wndPtr
->hSysMenu
);
4038 wndPtr
->hSysMenu
= 0;
4041 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4042 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4044 if( wndPtr
->hSysMenu
)
4047 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4049 /* Store the dummy sysmenu handle to facilitate the refresh */
4050 /* of the close button if the SC_CLOSE item change */
4051 menu
= MENU_GetMenu(retvalue
);
4053 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4055 WIN_ReleasePtr( wndPtr
);
4057 return bRevert
? 0 : retvalue
;
4061 /*******************************************************************
4062 * SetSystemMenu (USER32.@)
4064 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4066 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4068 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4070 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4071 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4072 WIN_ReleasePtr( wndPtr
);
4079 /**********************************************************************
4080 * GetMenu (USER32.@)
4082 HMENU WINAPI
GetMenu( HWND hWnd
)
4084 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4085 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4089 /**********************************************************************
4090 * GetMenuBarInfo (USER32.@)
4092 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4094 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4098 /**********************************************************************
4101 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4102 * SetWindowPos call that would result if SetMenu were called directly.
4104 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4106 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4108 if (hMenu
&& !IsMenu(hMenu
))
4110 WARN("hMenu %p is not a menu handle\n", hMenu
);
4113 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4116 hWnd
= WIN_GetFullHandle( hWnd
);
4117 if (GetCapture() == hWnd
)
4118 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4124 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4126 lpmenu
->hWnd
= hWnd
;
4127 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4129 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4134 /**********************************************************************
4135 * SetMenu (USER32.@)
4137 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4139 if(!MENU_SetMenu(hWnd
, hMenu
))
4142 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4143 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4148 /**********************************************************************
4149 * GetSubMenu (USER32.@)
4151 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4155 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4156 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4157 return lpmi
->hSubMenu
;
4161 /**********************************************************************
4162 * DrawMenuBar (USER32.@)
4164 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4167 HMENU hMenu
= GetMenu(hWnd
);
4169 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4171 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4173 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4174 lppop
->hwndOwner
= hWnd
;
4175 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4176 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4180 /***********************************************************************
4181 * DrawMenuBarTemp (USER32.@)
4185 * called by W98SE desk.cpl Control Panel Applet
4187 * Not 100% sure about the param names, but close.
4189 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4194 BOOL flat_menu
= FALSE
;
4196 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4199 hMenu
= GetMenu(hwnd
);
4202 hFont
= get_menu_font(FALSE
);
4204 lppop
= MENU_GetMenu( hMenu
);
4205 if (lppop
== NULL
|| lprect
== NULL
)
4207 retvalue
= GetSystemMetrics(SM_CYMENU
);
4211 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4213 hfontOld
= SelectObject( hDC
, hFont
);
4215 if (lppop
->Height
== 0)
4216 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4218 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4220 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4222 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4223 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4224 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4226 if (lppop
->nItems
== 0)
4228 retvalue
= GetSystemMetrics(SM_CYMENU
);
4232 for (i
= 0; i
< lppop
->nItems
; i
++)
4234 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4235 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4237 retvalue
= lppop
->Height
;
4240 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4244 /***********************************************************************
4245 * EndMenu (USER.187)
4246 * EndMenu (USER32.@)
4248 BOOL WINAPI
EndMenu(void)
4250 /* if we are in the menu code, and it is active */
4251 if (!fEndMenu
&& top_popup
)
4253 /* terminate the menu handling code */
4256 /* needs to be posted to wakeup the internal menu handler */
4257 /* which will now terminate the menu, in the event that */
4258 /* the main window was minimized, or lost focus, so we */
4259 /* don't end up with an orphaned menu */
4260 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4266 /***********************************************************************
4267 * LookupMenuHandle (USER.217)
4269 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4271 HMENU hmenu32
= HMENU_32(hmenu
);
4273 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4274 else return HMENU_16(hmenu32
);
4278 /**********************************************************************
4279 * LoadMenu (USER.150)
4281 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4287 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4288 if (!name
) return 0;
4290 instance
= GetExePtr( instance
);
4291 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4292 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4293 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4294 FreeResource16( handle
);
4299 /*****************************************************************
4300 * LoadMenuA (USER32.@)
4302 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4304 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4305 if (!hrsrc
) return 0;
4306 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
4310 /*****************************************************************
4311 * LoadMenuW (USER32.@)
4313 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4315 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4316 if (!hrsrc
) return 0;
4317 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
4321 /**********************************************************************
4322 * LoadMenuIndirect (USER.220)
4324 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4327 WORD version
, offset
;
4328 LPCSTR p
= (LPCSTR
)template;
4330 TRACE("(%p)\n", template );
4331 version
= GET_WORD(p
);
4335 WARN("version must be 0 for Win16\n" );
4338 offset
= GET_WORD(p
);
4339 p
+= sizeof(WORD
) + offset
;
4340 if (!(hMenu
= CreateMenu())) return 0;
4341 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4343 DestroyMenu( hMenu
);
4346 return HMENU_16(hMenu
);
4350 /**********************************************************************
4351 * LoadMenuIndirectW (USER32.@)
4353 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4356 WORD version
, offset
;
4357 LPCSTR p
= (LPCSTR
)template;
4359 version
= GET_WORD(p
);
4361 TRACE("%p, ver %d\n", template, version
);
4364 case 0: /* standard format is version of 0 */
4365 offset
= GET_WORD(p
);
4366 p
+= sizeof(WORD
) + offset
;
4367 if (!(hMenu
= CreateMenu())) return 0;
4368 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4370 DestroyMenu( hMenu
);
4374 case 1: /* extended format is version of 1 */
4375 offset
= GET_WORD(p
);
4376 p
+= sizeof(WORD
) + offset
;
4377 if (!(hMenu
= CreateMenu())) return 0;
4378 if (!MENUEX_ParseResource( p
, hMenu
))
4380 DestroyMenu( hMenu
);
4385 ERR("version %d not supported.\n", version
);
4391 /**********************************************************************
4392 * LoadMenuIndirectA (USER32.@)
4394 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4396 return LoadMenuIndirectW( template );
4400 /**********************************************************************
4403 BOOL WINAPI
IsMenu(HMENU hmenu
)
4405 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4409 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4415 /**********************************************************************
4416 * GetMenuItemInfo_common
4419 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4420 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4422 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4424 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4427 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4431 if( lpmii
->fMask
& MIIM_TYPE
) {
4432 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4433 WARN("invalid combination of fMask bits used\n");
4434 /* this does not happen on Win9x/ME */
4435 SetLastError( ERROR_INVALID_PARAMETER
);
4438 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4439 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4440 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4441 if( lpmii
->fType
& MFT_BITMAP
) {
4442 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4444 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4445 /* this does not happen on Win9x/ME */
4446 lpmii
->dwTypeData
= 0;
4451 /* copy the text string */
4452 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4454 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4457 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4459 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4465 len
= strlenW(menu
->text
);
4466 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4467 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4471 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4472 0, NULL
, NULL
) - 1;
4473 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4474 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4475 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4476 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4478 /* if we've copied a substring we return its length */
4479 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4480 if (lpmii
->cch
<= len
+ 1)
4485 /* return length of string */
4486 /* not on Win9x/ME if fType & MFT_BITMAP */
4492 if (lpmii
->fMask
& MIIM_FTYPE
)
4493 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4495 if (lpmii
->fMask
& MIIM_BITMAP
)
4496 lpmii
->hbmpItem
= menu
->hbmpItem
;
4498 if (lpmii
->fMask
& MIIM_STATE
)
4499 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4501 if (lpmii
->fMask
& MIIM_ID
)
4502 lpmii
->wID
= menu
->wID
;
4504 if (lpmii
->fMask
& MIIM_SUBMENU
)
4505 lpmii
->hSubMenu
= menu
->hSubMenu
;
4507 /* hSubMenu is always cleared
4508 * (not on Win9x/ME ) */
4509 lpmii
->hSubMenu
= 0;
4512 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4513 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4514 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4516 if (lpmii
->fMask
& MIIM_DATA
)
4517 lpmii
->dwItemData
= menu
->dwItemData
;
4522 /**********************************************************************
4523 * GetMenuItemInfoA (USER32.@)
4525 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4526 LPMENUITEMINFOA lpmii
)
4530 if( lpmii
->cbSize
!= sizeof( mii
) &&
4531 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4532 SetLastError( ERROR_INVALID_PARAMETER
);
4535 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4536 mii
.cbSize
= sizeof( mii
);
4537 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4538 (LPMENUITEMINFOW
)&mii
, FALSE
);
4539 mii
.cbSize
= lpmii
->cbSize
;
4540 memcpy( lpmii
, &mii
, mii
.cbSize
);
4544 /**********************************************************************
4545 * GetMenuItemInfoW (USER32.@)
4547 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4548 LPMENUITEMINFOW lpmii
)
4552 if( lpmii
->cbSize
!= sizeof( mii
) &&
4553 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4554 SetLastError( ERROR_INVALID_PARAMETER
);
4557 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4558 mii
.cbSize
= sizeof( mii
);
4559 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4560 mii
.cbSize
= lpmii
->cbSize
;
4561 memcpy( lpmii
, &mii
, mii
.cbSize
);
4566 /* set a menu item text from a ASCII or Unicode string */
4567 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4573 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4574 strcpyW( menu
->text
, text
);
4578 LPCSTR str
= (LPCSTR
)text
;
4579 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4580 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4581 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4586 /**********************************************************************
4587 * SetMenuItemInfo_common
4590 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4591 const MENUITEMINFOW
*lpmii
,
4594 if (!menu
) return FALSE
;
4596 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4598 if (lpmii
->fMask
& MIIM_TYPE
) {
4599 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4600 WARN("invalid combination of fMask bits used\n");
4601 /* this does not happen on Win9x/ME */
4602 SetLastError( ERROR_INVALID_PARAMETER
);
4606 /* Remove the old type bits and replace them with the new ones */
4607 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4608 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4610 if (IS_STRING_ITEM(menu
->fType
)) {
4611 HeapFree(GetProcessHeap(), 0, menu
->text
);
4612 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4613 } else if( (menu
->fType
) & MFT_BITMAP
)
4614 menu
->hbmpItem
= HBITMAP_32(LOWORD(lpmii
->dwTypeData
));
4617 if (lpmii
->fMask
& MIIM_FTYPE
) {
4618 if(( lpmii
->fType
& MFT_BITMAP
)) {
4619 SetLastError( ERROR_INVALID_PARAMETER
);
4622 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4623 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4625 if (lpmii
->fMask
& MIIM_STRING
) {
4626 /* free the string when used */
4627 HeapFree(GetProcessHeap(), 0, menu
->text
);
4628 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4631 if (lpmii
->fMask
& MIIM_STATE
)
4633 /* Other menu items having MFS_DEFAULT are not converted
4635 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4638 if (lpmii
->fMask
& MIIM_ID
)
4639 menu
->wID
= lpmii
->wID
;
4641 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4642 menu
->hSubMenu
= lpmii
->hSubMenu
;
4643 if (menu
->hSubMenu
) {
4644 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4646 subMenu
->wFlags
|= MF_POPUP
;
4647 menu
->fType
|= MF_POPUP
;
4650 SetLastError( ERROR_INVALID_PARAMETER
);
4655 menu
->fType
&= ~MF_POPUP
;
4658 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4660 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4661 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4663 if (lpmii
->fMask
& MIIM_DATA
)
4664 menu
->dwItemData
= lpmii
->dwItemData
;
4666 if (lpmii
->fMask
& MIIM_BITMAP
)
4667 menu
->hbmpItem
= lpmii
->hbmpItem
;
4669 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4670 menu
->fType
|= MFT_SEPARATOR
;
4672 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4676 /**********************************************************************
4677 * SetMenuItemInfoA (USER32.@)
4679 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4680 const MENUITEMINFOA
*lpmii
)
4684 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4686 if( lpmii
->cbSize
!= sizeof( mii
) &&
4687 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4688 SetLastError( ERROR_INVALID_PARAMETER
);
4691 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4692 if( lpmii
->cbSize
!= sizeof( mii
)) {
4693 mii
.cbSize
= sizeof( mii
);
4694 mii
.hbmpItem
= NULL
;
4696 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4697 (const MENUITEMINFOW
*)&mii
, FALSE
);
4700 /**********************************************************************
4701 * SetMenuItemInfoW (USER32.@)
4703 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4704 const MENUITEMINFOW
*lpmii
)
4708 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4710 if( lpmii
->cbSize
!= sizeof( mii
) &&
4711 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4712 SetLastError( ERROR_INVALID_PARAMETER
);
4715 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4716 if( lpmii
->cbSize
!= sizeof( mii
)) {
4717 mii
.cbSize
= sizeof( mii
);
4718 mii
.hbmpItem
= NULL
;
4720 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4721 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4724 /**********************************************************************
4725 * SetMenuDefaultItem (USER32.@)
4728 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4734 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4736 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4738 /* reset all default-item flags */
4740 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4742 item
->fState
&= ~MFS_DEFAULT
;
4745 /* no default item */
4754 if ( uItem
>= menu
->nItems
) return FALSE
;
4755 item
[uItem
].fState
|= MFS_DEFAULT
;
4760 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4762 if (item
->wID
== uItem
)
4764 item
->fState
|= MFS_DEFAULT
;
4773 /**********************************************************************
4774 * GetMenuDefaultItem (USER32.@)
4776 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4782 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4784 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4786 /* find default item */
4790 if (! item
) return -1;
4792 while ( !( item
->fState
& MFS_DEFAULT
) )
4795 if (i
>= menu
->nItems
) return -1;
4798 /* default: don't return disabled items */
4799 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4801 /* search rekursiv when needed */
4802 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4805 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4806 if ( -1 != ret
) return ret
;
4808 /* when item not found in submenu, return the popup item */
4810 return ( bypos
) ? i
: item
->wID
;
4815 /**********************************************************************
4816 * InsertMenuItemA (USER32.@)
4818 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4819 const MENUITEMINFOA
*lpmii
)
4824 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4826 if( lpmii
->cbSize
!= sizeof( mii
) &&
4827 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4828 SetLastError( ERROR_INVALID_PARAMETER
);
4831 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4832 if( lpmii
->cbSize
!= sizeof( mii
)) {
4833 mii
.cbSize
= sizeof( mii
);
4834 mii
.hbmpItem
= NULL
;
4837 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4838 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4842 /**********************************************************************
4843 * InsertMenuItemW (USER32.@)
4845 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4846 const MENUITEMINFOW
*lpmii
)
4851 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4853 if( lpmii
->cbSize
!= sizeof( mii
) &&
4854 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4855 SetLastError( ERROR_INVALID_PARAMETER
);
4858 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4859 if( lpmii
->cbSize
!= sizeof( mii
)) {
4860 mii
.cbSize
= sizeof( mii
);
4861 mii
.hbmpItem
= NULL
;
4864 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4865 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4868 /**********************************************************************
4869 * CheckMenuRadioItem (USER32.@)
4872 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4873 UINT first
, UINT last
, UINT check
,
4878 MENUITEM
*mi_first
= NULL
, *mi_check
;
4879 HMENU m_first
, m_check
;
4881 for (i
= first
; i
<= last
; i
++)
4888 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4889 if (!mi_first
) continue;
4890 mi_check
= mi_first
;
4896 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4897 if (!mi_check
) continue;
4900 if (m_first
!= m_check
) continue;
4901 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4905 mi_check
->fType
|= MFT_RADIOCHECK
;
4906 mi_check
->fState
|= MFS_CHECKED
;
4911 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4912 mi_check
->fState
&= ~MFS_CHECKED
;
4920 /**********************************************************************
4921 * GetMenuItemRect (USER32.@)
4923 * ATTENTION: Here, the returned values in rect are the screen
4924 * coordinates of the item just like if the menu was
4925 * always on the upper left side of the application.
4928 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4931 POPUPMENU
*itemMenu
;
4935 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4937 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4938 referenceHwnd
= hwnd
;
4942 itemMenu
= MENU_GetMenu(hMenu
);
4943 if (itemMenu
== NULL
)
4946 if(itemMenu
->hWnd
== 0)
4948 referenceHwnd
= itemMenu
->hWnd
;
4951 if ((rect
== NULL
) || (item
== NULL
))
4956 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4962 /**********************************************************************
4963 * SetMenuInfo (USER32.@)
4966 * MIM_APPLYTOSUBMENUS
4967 * actually use the items to draw the menu
4969 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4973 TRACE("(%p %p)\n", hMenu
, lpmi
);
4975 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4978 if (lpmi
->fMask
& MIM_BACKGROUND
)
4979 menu
->hbrBack
= lpmi
->hbrBack
;
4981 if (lpmi
->fMask
& MIM_HELPID
)
4982 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4984 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4985 menu
->cyMax
= lpmi
->cyMax
;
4987 if (lpmi
->fMask
& MIM_MENUDATA
)
4988 menu
->dwMenuData
= lpmi
->dwMenuData
;
4990 if (lpmi
->fMask
& MIM_STYLE
)
4992 menu
->dwStyle
= lpmi
->dwStyle
;
4993 if (menu
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
4994 if (menu
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
4995 if (menu
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5003 /**********************************************************************
5004 * GetMenuInfo (USER32.@)
5010 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5013 TRACE("(%p %p)\n", hMenu
, lpmi
);
5015 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
5018 if (lpmi
->fMask
& MIM_BACKGROUND
)
5019 lpmi
->hbrBack
= menu
->hbrBack
;
5021 if (lpmi
->fMask
& MIM_HELPID
)
5022 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5024 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5025 lpmi
->cyMax
= menu
->cyMax
;
5027 if (lpmi
->fMask
& MIM_MENUDATA
)
5028 lpmi
->dwMenuData
= menu
->dwMenuData
;
5030 if (lpmi
->fMask
& MIM_STYLE
)
5031 lpmi
->dwStyle
= menu
->dwStyle
;
5039 /**********************************************************************
5040 * SetMenuContextHelpId (USER32.@)
5042 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5046 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5048 if ((menu
= MENU_GetMenu(hMenu
)))
5050 menu
->dwContextHelpID
= dwContextHelpID
;
5057 /**********************************************************************
5058 * GetMenuContextHelpId (USER32.@)
5060 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5064 TRACE("(%p)\n", hMenu
);
5066 if ((menu
= MENU_GetMenu(hMenu
)))
5068 return menu
->dwContextHelpID
;
5073 /**********************************************************************
5074 * MenuItemFromPoint (USER32.@)
5076 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5078 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5081 /*FIXME: Do we have to handle hWnd here? */
5082 if (!menu
) return -1;
5083 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5088 /**********************************************************************
5089 * translate_accelerator
5091 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5092 BYTE fVirt
, WORD key
, WORD cmd
)
5097 if (wParam
!= key
) return FALSE
;
5099 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5100 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5101 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5103 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5105 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5107 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5113 if(fVirt
& FVIRTKEY
)
5115 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5116 wParam
, 0xff & HIWORD(lParam
));
5118 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5119 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5123 if (!(lParam
& 0x01000000)) /* no special_key */
5125 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5126 { /* ^^ ALT pressed */
5127 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5136 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5140 HMENU hMenu
, hSubMenu
, hSysMenu
;
5141 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5143 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5144 hSysMenu
= get_win_sys_menu( hWnd
);
5146 /* find menu item and ask application to initialize it */
5147 /* 1. in the system menu */
5148 hSubMenu
= hSysMenu
;
5150 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5154 if (!IsWindowEnabled(hWnd
))
5158 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5159 if(hSubMenu
!= hSysMenu
)
5161 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5162 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5163 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5165 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5168 else /* 2. in the window's menu */
5172 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5176 if (!IsWindowEnabled(hWnd
))
5180 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5181 if(hSubMenu
!= hMenu
)
5183 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5184 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5185 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5187 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5194 if (uSysStat
!= (UINT
)-1)
5196 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5203 if (uStat
!= (UINT
)-1)
5209 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5221 if( mesg
==WM_COMMAND
)
5223 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5224 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5226 else if( mesg
==WM_SYSCOMMAND
)
5228 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5229 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5233 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5234 * #0: unknown (please report!)
5235 * #1: for WM_KEYUP,WM_SYSKEYUP
5236 * #2: mouse is captured
5237 * #3: window is disabled
5238 * #4: it's a disabled system menu option
5239 * #5: it's a menu option, but window is iconic
5240 * #6: it's a menu option, but disabled
5242 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5244 ERR_(accel
)(" unknown reason - please report!\n");
5249 /**********************************************************************
5250 * TranslateAcceleratorA (USER32.@)
5251 * TranslateAccelerator (USER32.@)
5253 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5256 LPACCEL16 lpAccelTbl
;
5260 if (!hWnd
|| !msg
) return 0;
5262 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5264 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5268 wParam
= msg
->wParam
;
5270 switch (msg
->message
)
5279 char ch
= LOWORD(wParam
);
5281 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5282 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5290 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5291 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5295 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5296 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5298 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5303 /**********************************************************************
5304 * TranslateAcceleratorW (USER32.@)
5306 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5309 LPACCEL16 lpAccelTbl
;
5312 if (!hWnd
|| !msg
) return 0;
5314 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5316 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5320 switch (msg
->message
)
5332 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5333 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5337 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5338 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5340 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);