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 0x0001
120 #define TF_SUSPENDPOPUP 0x0002
121 #define TF_SKIPREMOVE 0x0004
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;
1509 if( !(menu
->dwStyle
& ( MNS_CHECKORBMP
| MNS_NOCHECK
)))
1510 bmprc
.left
+= GetSystemMetrics( SM_CXMENUCHECK
);
1512 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1513 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1516 bmprc
.top
= (rect
.bottom
- rect
.top
-
1517 lpitem
->bmpsize
.cy
) / 2;
1518 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1524 INT y
= rect
.top
+ rect
.bottom
;
1526 int checked
= FALSE
;
1527 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1528 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1529 /* Draw the check mark
1532 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1534 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1535 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1536 lpitem
->hUnCheckBit
;
1537 if (bm
) /* we have a custom bitmap */
1539 HDC hdcMem
= CreateCompatibleDC( hdc
);
1541 SelectObject( hdcMem
, bm
);
1542 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1543 check_bitmap_width
, check_bitmap_height
,
1544 hdcMem
, 0, 0, SRCCOPY
);
1548 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1551 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1552 check_bitmap_height
, 1, 1, NULL
);
1553 HDC hdcMem
= CreateCompatibleDC( hdc
);
1555 SelectObject( hdcMem
, bm
);
1556 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1557 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1558 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1559 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1560 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1561 hdcMem
, 0, 0, SRCCOPY
);
1567 if( lpitem
->hbmpItem
&&
1568 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1570 /* some applications make this assumption on the DC's origin */
1571 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1572 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1574 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1576 /* Draw the popup-menu arrow */
1577 if (lpitem
->fType
& MF_POPUP
)
1578 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1579 arrow_bitmap_height
);
1581 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1582 rect
.left
+= check_bitmap_width
;
1583 rect
.right
-= arrow_bitmap_width
;
1585 else if( lpitem
->hbmpItem
)
1586 { /* Draw the bitmap */
1589 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1590 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1592 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1594 /* process text if present */
1600 UINT uFormat
= (menuBar
) ?
1601 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1602 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1604 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1605 rect
.left
+= menu
->maxBmpSize
.cx
;
1607 if ( lpitem
->fState
& MFS_DEFAULT
)
1609 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1613 if( lpitem
->hbmpItem
)
1614 rect
.left
+= lpitem
->bmpsize
.cx
;
1615 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1616 rect
.left
+= menucharsize
.cx
;
1617 rect
.right
-= menucharsize
.cx
;
1620 for (i
= 0; lpitem
->text
[i
]; i
++)
1621 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1624 if(lpitem
->fState
& MF_GRAYED
)
1626 if (!(lpitem
->fState
& MF_HILITE
) )
1628 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1629 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1630 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1631 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1633 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1636 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1638 /* paint the shortcut text */
1639 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1641 if (lpitem
->text
[i
] == '\t')
1643 rect
.left
= lpitem
->xTab
;
1644 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1648 rect
.right
= lpitem
->xTab
;
1649 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1652 if(lpitem
->fState
& MF_GRAYED
)
1654 if (!(lpitem
->fState
& MF_HILITE
) )
1656 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1657 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1658 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1659 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1661 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1663 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1667 SelectObject (hdc
, hfontOld
);
1672 /***********************************************************************
1673 * MENU_DrawPopupMenu
1675 * Paint a popup menu.
1677 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1679 HBRUSH hPrevBrush
= 0;
1682 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1684 GetClientRect( hwnd
, &rect
);
1686 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1687 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1691 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1693 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1697 BOOL flat_menu
= FALSE
;
1699 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1701 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1703 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1705 if( (menu
= MENU_GetMenu( hmenu
)))
1707 /* draw menu items */
1714 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1715 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1716 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1718 /* draw scroll arrows */
1719 if (menu
->bScrolling
)
1720 MENU_DrawScrollArrows(menu
, hdc
);
1724 SelectObject( hdc
, hPrevBrush
);
1729 /***********************************************************************
1732 * Paint a menu bar. Returns the height of the menu bar.
1733 * called from [windows/nonclient.c]
1735 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1740 HMENU hMenu
= GetMenu(hwnd
);
1742 lppop
= MENU_GetMenu( hMenu
);
1743 if (lppop
== NULL
|| lprect
== NULL
)
1745 return GetSystemMetrics(SM_CYMENU
);
1750 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1752 if (lppop
->Height
== 0)
1753 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1755 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1757 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1758 return lppop
->Height
;
1761 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1765 /***********************************************************************
1768 * Display a popup menu.
1770 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
,
1771 INT x
, INT y
, INT xanchor
, INT yanchor
)
1779 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1780 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1782 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1783 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1785 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1786 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1789 /* store the owner for DrawItem */
1790 menu
->hwndOwner
= hwndOwner
;
1792 menu
->nScrollPos
= 0;
1793 MENU_PopupMenuCalcSize( menu
);
1795 /* adjust popup menu pos so that it fits within the desktop */
1797 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1798 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1800 /* FIXME: should use item rect */
1803 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1804 info
.cbSize
= sizeof(info
);
1805 GetMonitorInfoW( monitor
, &info
);
1806 if( x
+ width
> info
.rcWork
.right
)
1808 if( xanchor
&& x
>= width
- xanchor
)
1809 x
-= width
- xanchor
;
1811 if( x
+ width
> info
.rcWork
.right
)
1812 x
= info
.rcWork
.right
- width
;
1814 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1816 if( y
+ height
> info
.rcWork
.bottom
)
1818 if( yanchor
&& y
>= height
+ yanchor
)
1819 y
-= height
+ yanchor
;
1821 if( y
+ height
> info
.rcWork
.bottom
)
1822 y
= info
.rcWork
.bottom
- height
;
1824 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1826 /* NOTE: In Windows, top menu popup is not owned. */
1827 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1828 WS_POPUP
, x
, y
, width
, height
,
1829 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1831 if( !menu
->hWnd
) return FALSE
;
1832 if (!top_popup
) top_popup
= menu
->hWnd
;
1834 /* Display the window */
1836 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1837 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1838 UpdateWindow( menu
->hWnd
);
1843 /***********************************************************************
1844 * MENU_EnsureMenuItemVisible
1847 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1849 if (lppop
->bScrolling
)
1851 MENUITEM
*item
= &lppop
->items
[wIndex
];
1852 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1853 UINT nOldPos
= lppop
->nScrollPos
;
1855 UINT arrow_bitmap_height
;
1858 GetClientRect(lppop
->hWnd
, &rc
);
1860 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1861 arrow_bitmap_height
= bmp
.bmHeight
;
1863 rc
.top
+= arrow_bitmap_height
;
1864 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1866 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1867 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1870 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1871 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1872 MENU_DrawScrollArrows(lppop
, hdc
);
1874 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1876 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1877 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1878 MENU_DrawScrollArrows(lppop
, hdc
);
1884 /***********************************************************************
1887 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1888 BOOL sendMenuSelect
, HMENU topmenu
)
1893 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1895 lppop
= MENU_GetMenu( hmenu
);
1896 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1898 if (lppop
->FocusedItem
== wIndex
) return;
1899 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1900 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1901 if (!top_popup
) top_popup
= lppop
->hWnd
;
1903 SelectObject( hdc
, get_menu_font(FALSE
));
1905 /* Clear previous highlighted item */
1906 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1908 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1909 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1910 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1914 /* Highlight new item (if any) */
1915 lppop
->FocusedItem
= wIndex
;
1916 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1918 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1919 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1920 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1921 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1922 &lppop
->items
[wIndex
], lppop
->Height
,
1923 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1927 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1928 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1929 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1930 ip
->fType
| ip
->fState
|
1931 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1934 else if (sendMenuSelect
) {
1937 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1938 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1939 MENUITEM
*ip
= &ptm
->items
[pos
];
1940 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1941 ip
->fType
| ip
->fState
|
1942 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1946 ReleaseDC( lppop
->hWnd
, hdc
);
1950 /***********************************************************************
1951 * MENU_MoveSelection
1953 * Moves currently selected item according to the offset parameter.
1954 * If there is no selection then it should select the last item if
1955 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1957 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1962 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1964 menu
= MENU_GetMenu( hmenu
);
1965 if ((!menu
) || (!menu
->items
)) return;
1967 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1969 if( menu
->nItems
== 1 ) return; else
1970 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1972 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1974 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1979 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1980 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1981 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1983 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1989 /**********************************************************************
1992 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1995 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
1998 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
1999 TRACE("flags=%x str=%p\n", flags
, str
);
2001 if (IS_STRING_ITEM(flags
))
2003 LPWSTR prevText
= item
->text
;
2006 flags
|= MF_SEPARATOR
;
2012 /* Item beginning with a backspace is a help item */
2018 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
2020 strcpyW( text
, str
);
2023 item
->hbmpItem
= NULL
;
2024 HeapFree( GetProcessHeap(), 0, prevText
);
2026 else if(( flags
& MFT_BITMAP
)) {
2027 item
->hbmpItem
= HBITMAP_32(LOWORD(str
));
2028 /* setting bitmap clears text */
2029 HeapFree( GetProcessHeap(), 0, item
->text
);
2033 if (flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
2035 if (flags
& MF_OWNERDRAW
)
2036 item
->dwItemData
= (DWORD_PTR
)str
;
2038 item
->dwItemData
= 0;
2040 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
2041 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
2043 if (flags
& MF_POPUP
)
2045 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
2046 if (menu
) menu
->wFlags
|= MF_POPUP
;
2058 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
2060 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
2061 flags
|= MF_POPUP
; /* keep popup */
2063 item
->fType
= flags
& TYPE_MASK
;
2064 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2065 for ModifyMenu, but Windows accepts it */
2066 item
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
2068 /* Don't call SetRectEmpty here! */
2070 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
2075 /**********************************************************************
2078 * Insert (allocate) a new item into a menu.
2080 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2085 if (!(menu
= MENU_GetMenu(hMenu
)))
2088 /* Find where to insert new item */
2090 if (flags
& MF_BYPOSITION
) {
2091 if (pos
> menu
->nItems
)
2094 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2097 if (!(menu
= MENU_GetMenu( hMenu
)))
2102 /* Make sure that MDI system buttons stay on the right side.
2103 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2104 * regardless of their id.
2106 while (pos
> 0 && (menu
->items
[pos
- 1].fType
& MFT_BITMAP
) &&
2107 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2108 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2111 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2113 /* Create new items array */
2115 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2118 WARN("allocation failed\n" );
2121 if (menu
->nItems
> 0)
2123 /* Copy the old array into the new one */
2124 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2125 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2126 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2127 HeapFree( GetProcessHeap(), 0, menu
->items
);
2129 menu
->items
= newItems
;
2131 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2132 menu
->Height
= 0; /* force size recalculate */
2133 return &newItems
[pos
];
2137 /**********************************************************************
2138 * MENU_ParseResource
2140 * Parse a standard menu resource and add items to the menu.
2141 * Return a pointer to the end of the resource.
2143 * NOTE: flags is equivalent to the mtOption field
2145 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2153 flags
= GET_WORD(res
);
2154 end_flag
= flags
& MF_END
;
2155 /* Remove MF_END because it has the same value as MF_HILITE */
2157 res
+= sizeof(WORD
);
2158 if (!(flags
& MF_POPUP
))
2161 res
+= sizeof(WORD
);
2164 if (!unicode
) res
+= strlen(str
) + 1;
2165 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2166 if (flags
& MF_POPUP
)
2168 HMENU hSubMenu
= CreatePopupMenu();
2169 if (!hSubMenu
) return NULL
;
2170 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2172 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2173 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2175 else /* Not a popup */
2177 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2178 else AppendMenuW( hMenu
, flags
, id
,
2179 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2181 } while (!end_flag
);
2186 /**********************************************************************
2187 * MENUEX_ParseResource
2189 * Parse an extended menu resource and add items to the menu.
2190 * Return a pointer to the end of the resource.
2192 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2198 mii
.cbSize
= sizeof(mii
);
2199 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2200 mii
.fType
= GET_DWORD(res
);
2201 res
+= sizeof(DWORD
);
2202 mii
.fState
= GET_DWORD(res
);
2203 res
+= sizeof(DWORD
);
2204 mii
.wID
= GET_DWORD(res
);
2205 res
+= sizeof(DWORD
);
2206 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2207 res
+= sizeof(WORD
);
2208 /* Align the text on a word boundary. */
2209 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2210 mii
.dwTypeData
= (LPWSTR
) res
;
2211 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2212 /* Align the following fields on a dword boundary. */
2213 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2215 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2216 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2218 if (resinfo
& 1) { /* Pop-up? */
2219 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2220 res
+= sizeof(DWORD
);
2221 mii
.hSubMenu
= CreatePopupMenu();
2224 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2225 DestroyMenu(mii
.hSubMenu
);
2228 mii
.fMask
|= MIIM_SUBMENU
;
2229 mii
.fType
|= MF_POPUP
;
2231 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2233 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2234 mii
.wID
, mii
.fType
);
2235 mii
.fType
|= MF_SEPARATOR
;
2237 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2238 } while (!(resinfo
& MF_END
));
2243 /***********************************************************************
2246 * Return the handle of the selected sub-popup menu (if any).
2248 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2253 menu
= MENU_GetMenu( hmenu
);
2255 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2257 item
= &menu
->items
[menu
->FocusedItem
];
2258 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2259 return item
->hSubMenu
;
2264 /***********************************************************************
2265 * MENU_HideSubPopups
2267 * Hide the sub-popup menus of this menu.
2269 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2270 BOOL sendMenuSelect
)
2272 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2274 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2276 if (menu
&& top_popup
)
2282 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2284 item
= &menu
->items
[menu
->FocusedItem
];
2285 if (!(item
->fType
& MF_POPUP
) ||
2286 !(item
->fState
& MF_MOUSESELECT
)) return;
2287 item
->fState
&= ~MF_MOUSESELECT
;
2288 hsubmenu
= item
->hSubMenu
;
2291 submenu
= MENU_GetMenu( hsubmenu
);
2292 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
);
2293 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2294 DestroyWindow( submenu
->hWnd
);
2300 /***********************************************************************
2303 * Display the sub-menu of the selected item of this menu.
2304 * Return the handle of the submenu, or hmenu if no submenu to display.
2306 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2307 BOOL selectFirst
, UINT wFlags
)
2314 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2316 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2318 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2320 item
= &menu
->items
[menu
->FocusedItem
];
2321 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2324 /* message must be sent before using item,
2325 because nearly everything may be changed by the application ! */
2327 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2328 if (!(wFlags
& TPM_NONOTIFY
))
2329 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2330 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2332 item
= &menu
->items
[menu
->FocusedItem
];
2335 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2336 if (!(item
->fState
& MF_HILITE
))
2338 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2339 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2341 SelectObject( hdc
, get_menu_font(FALSE
));
2343 item
->fState
|= MF_HILITE
;
2344 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2345 ReleaseDC( menu
->hWnd
, hdc
);
2347 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2350 item
->fState
|= MF_MOUSESELECT
;
2352 if (IS_SYSTEM_MENU(menu
))
2354 MENU_InitSysMenuPopup(item
->hSubMenu
,
2355 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2356 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2358 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2359 rect
.top
= rect
.bottom
;
2360 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2361 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2365 GetWindowRect( menu
->hWnd
, &rect
);
2366 if (menu
->wFlags
& MF_POPUP
)
2368 RECT rc
= item
->rect
;
2370 MENU_AdjustMenuItemRect(menu
, &rc
);
2372 /* The first item in the popup menu has to be at the
2373 same y position as the focused menu item */
2374 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2375 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2376 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2377 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2378 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2382 rect
.left
+= item
->rect
.left
;
2383 rect
.top
+= item
->rect
.bottom
;
2384 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2385 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2389 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
,
2390 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2392 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2393 return item
->hSubMenu
;
2398 /**********************************************************************
2401 HWND
MENU_IsMenuActive(void)
2406 /***********************************************************************
2409 * Walks menu chain trying to find a menu pt maps to.
2411 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2413 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2414 UINT item
= menu
->FocusedItem
;
2417 /* try subpopup first (if any) */
2418 ret
= (item
!= NO_SELECTED_ITEM
&&
2419 (menu
->items
[item
].fType
& MF_POPUP
) &&
2420 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2421 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2423 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2425 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2426 if( menu
->wFlags
& MF_POPUP
)
2428 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2430 else if (ht
== HTSYSMENU
)
2431 ret
= get_win_sys_menu( menu
->hWnd
);
2432 else if (ht
== HTMENU
)
2433 ret
= GetMenu( menu
->hWnd
);
2438 /***********************************************************************
2439 * MENU_ExecFocusedItem
2441 * Execute a menu item (for instance when user pressed Enter).
2442 * Return the wID of the executed item. Otherwise, -1 indicating
2443 * that no menu item was executed, -2 if a popup is shown;
2444 * Have to receive the flags for the TrackPopupMenu options to avoid
2445 * sending unwanted message.
2448 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2451 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2453 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2455 if (!menu
|| !menu
->nItems
||
2456 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2458 item
= &menu
->items
[menu
->FocusedItem
];
2460 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2462 if (!(item
->fType
& MF_POPUP
))
2464 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2466 /* If TPM_RETURNCMD is set you return the id, but
2467 do not send a message to the owner */
2468 if(!(wFlags
& TPM_RETURNCMD
))
2470 if( menu
->wFlags
& MF_SYSMENU
)
2471 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2472 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2475 if (menu
->dwStyle
& MNS_NOTIFYBYPOS
)
2476 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2479 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2487 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2494 /***********************************************************************
2495 * MENU_SwitchTracking
2497 * Helper function for menu navigation routines.
2499 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
)
2501 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2502 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2504 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2506 if( pmt
->hTopMenu
!= hPtMenu
&&
2507 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2509 /* both are top level menus (system and menu-bar) */
2510 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2511 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2512 pmt
->hTopMenu
= hPtMenu
;
2514 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
);
2515 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2519 /***********************************************************************
2522 * Return TRUE if we can go on with menu tracking.
2524 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2526 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2531 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2534 if( IS_SYSTEM_MENU(ptmenu
) )
2535 item
= ptmenu
->items
;
2537 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2541 if( ptmenu
->FocusedItem
!= id
)
2542 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2544 /* If the popup menu is not already "popped" */
2545 if(!(item
->fState
& MF_MOUSESELECT
))
2547 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2552 /* Else the click was on the menu bar, finish the tracking */
2557 /***********************************************************************
2560 * Return the value of MENU_ExecFocusedItem if
2561 * the selected item was not a popup. Else open the popup.
2562 * A -1 return value indicates that we go on with menu tracking.
2565 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2567 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2572 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2575 if( IS_SYSTEM_MENU(ptmenu
) )
2576 item
= ptmenu
->items
;
2578 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2580 if( item
&& (ptmenu
->FocusedItem
== id
))
2582 debug_print_menuitem ("FocusedItem: ", item
, "");
2584 if( !(item
->fType
& MF_POPUP
) )
2586 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2587 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2588 return executedMenuId
;
2591 /* If we are dealing with the top-level menu */
2592 /* and this is a click on an already "popped" item: */
2593 /* Stop the menu tracking and close the opened submenus */
2594 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2597 ptmenu
->bTimeToHide
= TRUE
;
2603 /***********************************************************************
2606 * Return TRUE if we can go on with menu tracking.
2608 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2610 UINT id
= NO_SELECTED_ITEM
;
2611 POPUPMENU
*ptmenu
= NULL
;
2615 ptmenu
= MENU_GetMenu( hPtMenu
);
2616 if( IS_SYSTEM_MENU(ptmenu
) )
2619 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2622 if( id
== NO_SELECTED_ITEM
)
2624 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2625 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2628 else if( ptmenu
->FocusedItem
!= id
)
2630 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2631 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2637 /***********************************************************************
2640 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2642 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
)
2644 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2647 /* When skipping left, we need to do something special after the
2649 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2653 /* When skipping right, for the non-system menu, we need to
2654 handle the last non-special menu item (ie skip any window
2655 icons such as MDI maximize, restore or close) */
2656 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2658 int i
= menu
->FocusedItem
+ 1;
2659 while (i
< menu
->nItems
) {
2660 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2661 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2665 if (i
== menu
->nItems
) {
2669 /* When skipping right, we need to cater for the system menu */
2670 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2672 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2679 MDINEXTMENU next_menu
;
2684 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2685 next_menu
.hmenuNext
= 0;
2686 next_menu
.hwndNext
= 0;
2687 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2689 TRACE("%p [%p] -> %p [%p]\n",
2690 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2692 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2694 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2695 hNewWnd
= pmt
->hOwnerWnd
;
2696 if( IS_SYSTEM_MENU(menu
) )
2698 /* switch to the menu bar */
2700 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2704 menu
= MENU_GetMenu( hNewMenu
);
2705 id
= menu
->nItems
- 1;
2707 /* Skip backwards over any system predefined icons,
2708 eg. MDI close, restore etc icons */
2710 (menu
->items
[id
].wID
>= SC_SIZE
&&
2711 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2714 else if (style
& WS_SYSMENU
)
2716 /* switch to the system menu */
2717 hNewMenu
= get_win_sys_menu( hNewWnd
);
2721 else /* application returned a new menu to switch to */
2723 hNewMenu
= next_menu
.hmenuNext
;
2724 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2726 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2728 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2730 if (style
& WS_SYSMENU
&&
2731 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2733 /* get the real system menu */
2734 hNewMenu
= get_win_sys_menu(hNewWnd
);
2736 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2738 /* FIXME: Not sure what to do here;
2739 * perhaps try to track hNewMenu as a popup? */
2741 TRACE(" -- got confused.\n");
2748 if( hNewMenu
!= pmt
->hTopMenu
)
2750 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2752 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2753 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2756 if( hNewWnd
!= pmt
->hOwnerWnd
)
2758 pmt
->hOwnerWnd
= hNewWnd
;
2759 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2762 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2763 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2770 /***********************************************************************
2773 * The idea is not to show the popup if the next input message is
2774 * going to hide it anyway.
2776 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2780 msg
.hwnd
= pmt
->hOwnerWnd
;
2782 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2783 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2788 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2789 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2791 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2792 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2793 if( msg
.message
== WM_KEYDOWN
&&
2794 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2796 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2803 /* failures go through this */
2804 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2808 /***********************************************************************
2811 * Handle a VK_ESCAPE key event in a menu.
2813 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2815 BOOL bEndMenu
= TRUE
;
2817 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2819 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2821 if (menu
->wFlags
& MF_POPUP
)
2823 HMENU hmenutmp
, hmenuprev
;
2825 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2827 /* close topmost popup */
2828 while (hmenutmp
!= pmt
->hCurrentMenu
)
2830 hmenuprev
= hmenutmp
;
2831 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2834 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2835 pmt
->hCurrentMenu
= hmenuprev
;
2843 /***********************************************************************
2846 * Handle a VK_LEFT key event in a menu.
2848 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2851 HMENU hmenutmp
, hmenuprev
;
2854 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2855 menu
= MENU_GetMenu( hmenutmp
);
2857 /* Try to move 1 column left (if possible) */
2858 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2859 NO_SELECTED_ITEM
) {
2861 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2866 /* close topmost popup */
2867 while (hmenutmp
!= pmt
->hCurrentMenu
)
2869 hmenuprev
= hmenutmp
;
2870 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2873 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2874 pmt
->hCurrentMenu
= hmenuprev
;
2876 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2878 /* move menu bar selection if no more popups are left */
2880 if( !MENU_DoNextMenu( pmt
, VK_LEFT
) )
2881 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2883 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2885 /* A sublevel menu was displayed - display the next one
2886 * unless there is another displacement coming up */
2888 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2889 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2890 pmt
->hTopMenu
, TRUE
, wFlags
);
2896 /***********************************************************************
2899 * Handle a VK_RIGHT key event in a menu.
2901 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2904 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2907 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2909 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2910 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2912 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2914 /* If already displaying a popup, try to display sub-popup */
2916 hmenutmp
= pmt
->hCurrentMenu
;
2917 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2919 /* if subpopup was displayed then we are done */
2920 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2923 /* Check to see if there's another column */
2924 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2925 NO_SELECTED_ITEM
) {
2926 TRACE("Going to %d.\n", nextcol
);
2927 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2932 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2934 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2936 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2937 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2938 } else hmenutmp
= 0;
2940 /* try to move to the next item */
2941 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
) )
2942 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2944 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2945 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2946 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2947 pmt
->hTopMenu
, TRUE
, wFlags
);
2951 /***********************************************************************
2954 * Menu tracking code.
2956 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2957 HWND hwnd
, const RECT
*lprect
)
2962 INT executedMenuId
= -1;
2964 BOOL enterIdleSent
= FALSE
;
2968 mt
.hCurrentMenu
= hmenu
;
2969 mt
.hTopMenu
= hmenu
;
2970 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2974 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2975 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2978 if (!(menu
= MENU_GetMenu( hmenu
)))
2980 WARN("Invalid menu handle %p\n", hmenu
);
2981 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2985 if (wFlags
& TPM_BUTTONDOWN
)
2987 /* Get the result in order to start the tracking or not */
2988 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2989 fEndMenu
= !fRemove
;
2992 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
2994 /* owner may not be visible when tracking a popup, so use the menu itself */
2995 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
2996 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3000 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3001 if (!menu
) /* sometimes happens if I do a window manager close */
3004 /* we have to keep the message in the queue until it's
3005 * clear that menu loop is not over yet. */
3009 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3011 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3012 /* remove the message from the queue */
3013 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3019 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
3020 enterIdleSent
= TRUE
;
3021 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3027 /* check if EndMenu() tried to cancel us, by posting this message */
3028 if(msg
.message
== WM_CANCELMODE
)
3030 /* we are now out of the loop */
3033 /* remove the message from the queue */
3034 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3036 /* break out of internal loop, ala ESCAPE */
3040 TranslateMessage( &msg
);
3043 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3044 enterIdleSent
=FALSE
;
3047 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3050 * Use the mouse coordinates in lParam instead of those in the MSG
3051 * struct to properly handle synthetic messages. They are already
3052 * in screen coordinates.
3054 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3055 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3057 /* Find a menu for this mouse event */
3058 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3062 /* no WM_NC... messages in captured state */
3064 case WM_RBUTTONDBLCLK
:
3065 case WM_RBUTTONDOWN
:
3066 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3068 case WM_LBUTTONDBLCLK
:
3069 case WM_LBUTTONDOWN
:
3070 /* If the message belongs to the menu, removes it from the queue */
3071 /* Else, end menu tracking */
3072 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3073 fEndMenu
= !fRemove
;
3077 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3080 /* Check if a menu was selected by the mouse */
3083 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3084 TRACE("executedMenuId %d\n", executedMenuId
);
3086 /* End the loop if executedMenuId is an item ID */
3087 /* or if the job was done (executedMenuId = 0). */
3088 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3090 /* No menu was selected by the mouse */
3091 /* if the function was called by TrackPopupMenu, continue
3092 with the menu tracking. If not, stop it */
3094 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3099 /* the selected menu item must be changed every time */
3100 /* the mouse moves. */
3103 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3105 } /* switch(msg.message) - mouse */
3107 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3109 fRemove
= TRUE
; /* Keyboard messages are always removed */
3122 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3123 NO_SELECTED_ITEM
, FALSE
, 0 );
3124 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3125 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3129 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3131 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3132 if (!(menu
->wFlags
& MF_POPUP
))
3133 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3134 else /* otherwise try to move selection */
3135 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3136 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3140 MENU_KeyLeft( &mt
, wFlags
);
3144 MENU_KeyRight( &mt
, wFlags
);
3148 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3154 hi
.cbSize
= sizeof(HELPINFO
);
3155 hi
.iContextType
= HELPINFO_MENUITEM
;
3156 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3159 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3160 hi
.hItemHandle
= hmenu
;
3161 hi
.dwContextId
= menu
->dwContextHelpID
;
3162 hi
.MousePos
= msg
.pt
;
3163 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3170 break; /* WM_KEYDOWN */
3177 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3179 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3180 fEndMenu
= (executedMenuId
!= -2);
3185 /* Hack to avoid control chars. */
3186 /* We will find a better way real soon... */
3187 if (msg
.wParam
< 32) break;
3189 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3190 LOWORD(msg
.wParam
), FALSE
);
3191 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3192 else if (pos
== (UINT
)-1) MessageBeep(0);
3195 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3197 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3198 fEndMenu
= (executedMenuId
!= -2);
3202 } /* switch(msg.message) - kbd */
3206 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3207 DispatchMessageW( &msg
);
3211 if (!fEndMenu
) fRemove
= TRUE
;
3213 /* finally remove message from the queue */
3215 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3216 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3217 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3220 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3222 /* If dropdown is still painted and the close box is clicked on
3223 then the menu will be destroyed as part of the DispatchMessage above.
3224 This will then invalidate the menu handle in mt.hTopMenu. We should
3225 check for this first. */
3226 if( IsMenu( mt
.hTopMenu
) )
3228 menu
= MENU_GetMenu( mt
.hTopMenu
);
3230 if( IsWindow( mt
.hOwnerWnd
) )
3232 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
);
3234 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3236 DestroyWindow( menu
->hWnd
);
3239 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3240 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
3243 /* Reset the variable for hiding menu */
3244 if( menu
) menu
->bTimeToHide
= FALSE
;
3247 /* The return value is only used by TrackPopupMenu */
3248 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3249 if (executedMenuId
== -1) executedMenuId
= 0;
3250 return executedMenuId
;
3253 /***********************************************************************
3256 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3260 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3264 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3265 if (!(wFlags
& TPM_NONOTIFY
))
3266 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3268 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3270 if (!(wFlags
& TPM_NONOTIFY
))
3272 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3273 /* If an app changed/recreated menu bar entries in WM_INITMENU
3274 * menu sizes will be recalculated once the menu created/shown.
3278 /* This makes the menus of applications built with Delphi work.
3279 * It also enables menus to be displayed in more than one window,
3280 * but there are some bugs left that need to be fixed in this case.
3282 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3286 /***********************************************************************
3289 static BOOL
MENU_ExitTracking(HWND hWnd
)
3291 TRACE("hwnd=%p\n", hWnd
);
3293 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3299 /***********************************************************************
3300 * MENU_TrackMouseMenuBar
3302 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3304 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3306 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3307 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3309 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3313 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3314 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3315 MENU_ExitTracking(hWnd
);
3320 /***********************************************************************
3321 * MENU_TrackKbdMenuBar
3323 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3325 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3327 UINT uItem
= NO_SELECTED_ITEM
;
3329 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3331 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3333 /* find window that has a menu */
3335 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3336 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3338 /* check if we have to track a system menu */
3340 hTrackMenu
= GetMenu( hwnd
);
3341 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3343 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3344 hTrackMenu
= get_win_sys_menu( hwnd
);
3346 wParam
|= HTSYSMENU
; /* prevent item lookup */
3349 if (!IsMenu( hTrackMenu
)) return;
3351 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3353 if( wChar
&& wChar
!= ' ' )
3355 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3356 if ( uItem
>= (UINT
)(-2) )
3358 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3359 /* schedule end of menu tracking */
3360 wFlags
|= TF_ENDMENU
;
3365 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3367 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3369 if( uItem
== NO_SELECTED_ITEM
)
3370 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3372 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3376 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3377 MENU_ExitTracking( hwnd
);
3381 /**********************************************************************
3382 * TrackPopupMenu (USER32.@)
3384 * Like the win32 API, the function return the command ID only if the
3385 * flag TPM_RETURNCMD is on.
3388 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3389 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3393 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3394 hMenu
, wFlags
, x
, y
, nReserved
, hWnd
, wine_dbgstr_rect(lpRect
));
3396 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3398 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3399 if (!(wFlags
& TPM_NONOTIFY
))
3400 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3402 if (MENU_ShowPopup( hWnd
, hMenu
, 0, x
, y
, 0, 0 ))
3403 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3404 MENU_ExitTracking(hWnd
);
3409 /**********************************************************************
3410 * TrackPopupMenuEx (USER32.@)
3412 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3413 HWND hWnd
, LPTPMPARAMS lpTpm
)
3415 FIXME("not fully implemented\n" );
3416 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3417 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3420 /***********************************************************************
3423 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3425 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3427 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3433 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3434 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3438 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3439 return MA_NOACTIVATE
;
3444 BeginPaint( hwnd
, &ps
);
3445 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3446 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3447 EndPaint( hwnd
, &ps
);
3454 /* zero out global pointer in case resident popup window was destroyed. */
3455 if (hwnd
== top_popup
) top_popup
= 0;
3462 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3465 SetWindowLongPtrW( hwnd
, 0, 0 );
3468 case MM_SETMENUHANDLE
:
3469 SetWindowLongPtrW( hwnd
, 0, wParam
);
3472 case MM_GETMENUHANDLE
:
3473 return GetWindowLongPtrW( hwnd
, 0 );
3476 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3482 /***********************************************************************
3483 * MENU_GetMenuBarHeight
3485 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3487 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3488 INT orgX
, INT orgY
)
3494 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3496 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3498 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3499 SelectObject( hdc
, get_menu_font(FALSE
));
3500 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3501 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3502 ReleaseDC( hwnd
, hdc
);
3503 return lppop
->Height
;
3507 /*******************************************************************
3508 * ChangeMenuA (USER32.@)
3510 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3511 UINT id
, UINT flags
)
3513 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3514 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3516 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3517 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3519 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3520 flags
& MF_BYPOSITION
? pos
: id
,
3521 flags
& ~MF_REMOVE
);
3522 /* Default: MF_INSERT */
3523 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3527 /*******************************************************************
3528 * ChangeMenuW (USER32.@)
3530 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR 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 AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3536 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3537 if (flags
& MF_CHANGE
) return ModifyMenuW(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 InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3547 /*******************************************************************
3548 * CheckMenuItem (USER32.@)
3550 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3555 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3556 ret
= item
->fState
& MF_CHECKED
;
3557 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3558 else item
->fState
&= ~MF_CHECKED
;
3563 /**********************************************************************
3564 * EnableMenuItem (USER32.@)
3566 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3572 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3574 /* Get the Popupmenu to access the owner menu */
3575 if (!(menu
= MENU_GetMenu(hMenu
)))
3578 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3581 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3582 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3584 /* If the close item in the system menu change update the close button */
3585 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3587 if (menu
->hSysMenuOwner
!= 0)
3590 POPUPMENU
* parentMenu
;
3592 /* Get the parent menu to access*/
3593 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3596 /* Refresh the frame to reflect the change */
3597 GetWindowRect(parentMenu
->hWnd
, &rc
);
3598 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3600 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3608 /*******************************************************************
3609 * GetMenuStringA (USER32.@)
3611 INT WINAPI
GetMenuStringA(
3612 HMENU hMenu
, /* [in] menuhandle */
3613 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3614 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3615 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3616 UINT wFlags
/* [in] MF_ flags */
3620 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3621 if (str
&& nMaxSiz
) str
[0] = '\0';
3622 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3623 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3626 if (!item
->text
) return 0;
3627 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3628 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3630 TRACE("returning %s\n", debugstr_a(str
));
3635 /*******************************************************************
3636 * GetMenuStringW (USER32.@)
3638 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3639 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3643 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3644 if (str
&& nMaxSiz
) str
[0] = '\0';
3645 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3646 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3649 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3650 if( !(item
->text
)) {
3654 lstrcpynW( str
, item
->text
, nMaxSiz
);
3655 TRACE("returning %s\n", debugstr_w(str
));
3656 return strlenW(str
);
3660 /**********************************************************************
3661 * HiliteMenuItem (USER32.@)
3663 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3667 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3668 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3669 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3670 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3671 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
);
3672 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3677 /**********************************************************************
3678 * GetMenuState (USER32.@)
3680 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3683 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3684 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3685 debug_print_menuitem (" item: ", item
, "");
3686 if (item
->fType
& MF_POPUP
)
3688 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3689 if (!menu
) return -1;
3690 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3694 /* We used to (from way back then) mask the result to 0xff. */
3695 /* I don't know why and it seems wrong as the documented */
3696 /* return flag MF_SEPARATOR is outside that mask. */
3697 return (item
->fType
| item
->fState
);
3702 /**********************************************************************
3703 * GetMenuItemCount (USER32.@)
3705 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3707 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3708 if (!menu
) return -1;
3709 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3710 return menu
->nItems
;
3714 /**********************************************************************
3715 * GetMenuItemID (USER32.@)
3717 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3721 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3722 if (lpmi
->fType
& MF_POPUP
) return -1;
3728 /*******************************************************************
3729 * InsertMenuW (USER32.@)
3731 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3732 UINT_PTR id
, LPCWSTR str
)
3736 if (IS_STRING_ITEM(flags
) && str
)
3737 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3738 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3739 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3740 hMenu
, pos
, flags
, id
, str
);
3742 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3744 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3746 RemoveMenu( hMenu
, pos
, flags
);
3750 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3755 /*******************************************************************
3756 * InsertMenuA (USER32.@)
3758 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3759 UINT_PTR id
, LPCSTR str
)
3763 if (IS_STRING_ITEM(flags
) && str
)
3765 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3766 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3769 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3770 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3771 HeapFree( GetProcessHeap(), 0, newstr
);
3775 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3779 /*******************************************************************
3780 * AppendMenuA (USER32.@)
3782 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3783 UINT_PTR id
, LPCSTR data
)
3785 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3789 /*******************************************************************
3790 * AppendMenuW (USER32.@)
3792 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3793 UINT_PTR id
, LPCWSTR data
)
3795 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3799 /**********************************************************************
3800 * RemoveMenu (USER32.@)
3802 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3807 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3808 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3809 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3813 MENU_FreeItemData( item
);
3815 if (--menu
->nItems
== 0)
3817 HeapFree( GetProcessHeap(), 0, menu
->items
);
3822 while(nPos
< menu
->nItems
)
3828 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3829 menu
->nItems
* sizeof(MENUITEM
) );
3835 /**********************************************************************
3836 * DeleteMenu (USER32.@)
3838 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3840 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3841 if (!item
) return FALSE
;
3842 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3843 /* nPos is now the position of the item */
3844 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3849 /*******************************************************************
3850 * ModifyMenuW (USER32.@)
3852 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3853 UINT_PTR id
, LPCWSTR str
)
3857 if (IS_STRING_ITEM(flags
))
3858 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3860 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3862 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3863 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3864 return MENU_SetItemData( item
, flags
, id
, str
);
3868 /*******************************************************************
3869 * ModifyMenuA (USER32.@)
3871 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3872 UINT_PTR id
, LPCSTR str
)
3876 if (IS_STRING_ITEM(flags
) && str
)
3878 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3879 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3882 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3883 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3884 HeapFree( GetProcessHeap(), 0, newstr
);
3888 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3892 /**********************************************************************
3893 * CreatePopupMenu (USER32.@)
3895 HMENU WINAPI
CreatePopupMenu(void)
3900 if (!(hmenu
= CreateMenu())) return 0;
3901 menu
= MENU_GetMenu( hmenu
);
3902 menu
->wFlags
|= MF_POPUP
;
3903 menu
->bTimeToHide
= FALSE
;
3908 /**********************************************************************
3909 * GetMenuCheckMarkDimensions (USER.417)
3910 * GetMenuCheckMarkDimensions (USER32.@)
3912 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3914 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3918 /**********************************************************************
3919 * SetMenuItemBitmaps (USER32.@)
3921 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3922 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3926 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3928 if (!hNewCheck
&& !hNewUnCheck
)
3930 item
->fState
&= ~MF_USECHECKBITMAPS
;
3932 else /* Install new bitmaps */
3934 item
->hCheckBit
= hNewCheck
;
3935 item
->hUnCheckBit
= hNewUnCheck
;
3936 item
->fState
|= MF_USECHECKBITMAPS
;
3942 /**********************************************************************
3943 * CreateMenu (USER32.@)
3945 HMENU WINAPI
CreateMenu(void)
3949 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3950 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3952 ZeroMemory(menu
, sizeof(POPUPMENU
));
3953 menu
->wMagic
= MENU_MAGIC
;
3954 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3955 menu
->bTimeToHide
= FALSE
;
3957 TRACE("return %p\n", hMenu
);
3963 /**********************************************************************
3964 * DestroyMenu (USER32.@)
3966 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3968 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3970 TRACE("(%p)\n", hMenu
);
3973 if (!lppop
) return FALSE
;
3975 lppop
->wMagic
= 0; /* Mark it as destroyed */
3977 /* DestroyMenu should not destroy system menu popup owner */
3978 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
3980 DestroyWindow( lppop
->hWnd
);
3984 if (lppop
->items
) /* recursively destroy submenus */
3987 MENUITEM
*item
= lppop
->items
;
3988 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
3990 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
3991 MENU_FreeItemData( item
);
3993 HeapFree( GetProcessHeap(), 0, lppop
->items
);
3995 USER_HEAP_FREE( hMenu
);
4000 /**********************************************************************
4001 * GetSystemMenu (USER32.@)
4003 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4005 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4008 if (wndPtr
== WND_DESKTOP
) return 0;
4009 if (wndPtr
== WND_OTHER_PROCESS
)
4011 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4015 if (wndPtr
->hSysMenu
&& bRevert
)
4017 DestroyMenu(wndPtr
->hSysMenu
);
4018 wndPtr
->hSysMenu
= 0;
4021 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4022 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4024 if( wndPtr
->hSysMenu
)
4027 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4029 /* Store the dummy sysmenu handle to facilitate the refresh */
4030 /* of the close button if the SC_CLOSE item change */
4031 menu
= MENU_GetMenu(retvalue
);
4033 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4035 WIN_ReleasePtr( wndPtr
);
4037 return bRevert
? 0 : retvalue
;
4041 /*******************************************************************
4042 * SetSystemMenu (USER32.@)
4044 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4046 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4048 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4050 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4051 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4052 WIN_ReleasePtr( wndPtr
);
4059 /**********************************************************************
4060 * GetMenu (USER32.@)
4062 HMENU WINAPI
GetMenu( HWND hWnd
)
4064 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4065 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4069 /**********************************************************************
4070 * GetMenuBarInfo (USER32.@)
4072 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4074 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4078 /**********************************************************************
4081 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4082 * SetWindowPos call that would result if SetMenu were called directly.
4084 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4086 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4088 if (hMenu
&& !IsMenu(hMenu
))
4090 WARN("hMenu %p is not a menu handle\n", hMenu
);
4093 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4096 hWnd
= WIN_GetFullHandle( hWnd
);
4097 if (GetCapture() == hWnd
)
4098 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4104 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4106 lpmenu
->hWnd
= hWnd
;
4107 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4109 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4114 /**********************************************************************
4115 * SetMenu (USER32.@)
4117 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4119 if(!MENU_SetMenu(hWnd
, hMenu
))
4122 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4123 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4128 /**********************************************************************
4129 * GetSubMenu (USER32.@)
4131 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4135 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4136 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4137 return lpmi
->hSubMenu
;
4141 /**********************************************************************
4142 * DrawMenuBar (USER32.@)
4144 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4147 HMENU hMenu
= GetMenu(hWnd
);
4149 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4151 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4153 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4154 lppop
->hwndOwner
= hWnd
;
4155 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4156 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4160 /***********************************************************************
4161 * DrawMenuBarTemp (USER32.@)
4165 * called by W98SE desk.cpl Control Panel Applet
4167 * Not 100% sure about the param names, but close.
4169 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4174 BOOL flat_menu
= FALSE
;
4176 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4179 hMenu
= GetMenu(hwnd
);
4182 hFont
= get_menu_font(FALSE
);
4184 lppop
= MENU_GetMenu( hMenu
);
4185 if (lppop
== NULL
|| lprect
== NULL
)
4187 retvalue
= GetSystemMetrics(SM_CYMENU
);
4191 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4193 hfontOld
= SelectObject( hDC
, hFont
);
4195 if (lppop
->Height
== 0)
4196 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4198 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4200 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4202 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4203 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4204 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4206 if (lppop
->nItems
== 0)
4208 retvalue
= GetSystemMetrics(SM_CYMENU
);
4212 for (i
= 0; i
< lppop
->nItems
; i
++)
4214 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4215 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4217 retvalue
= lppop
->Height
;
4220 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4224 /***********************************************************************
4225 * EndMenu (USER.187)
4226 * EndMenu (USER32.@)
4228 BOOL WINAPI
EndMenu(void)
4230 /* if we are in the menu code, and it is active */
4231 if (!fEndMenu
&& top_popup
)
4233 /* terminate the menu handling code */
4236 /* needs to be posted to wakeup the internal menu handler */
4237 /* which will now terminate the menu, in the event that */
4238 /* the main window was minimized, or lost focus, so we */
4239 /* don't end up with an orphaned menu */
4240 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4246 /***********************************************************************
4247 * LookupMenuHandle (USER.217)
4249 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4251 HMENU hmenu32
= HMENU_32(hmenu
);
4253 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4254 else return HMENU_16(hmenu32
);
4258 /**********************************************************************
4259 * LoadMenu (USER.150)
4261 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4267 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4268 if (!name
) return 0;
4270 instance
= GetExePtr( instance
);
4271 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4272 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4273 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4274 FreeResource16( handle
);
4279 /*****************************************************************
4280 * LoadMenuA (USER32.@)
4282 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4284 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4285 if (!hrsrc
) return 0;
4286 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
4290 /*****************************************************************
4291 * LoadMenuW (USER32.@)
4293 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4295 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4296 if (!hrsrc
) return 0;
4297 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
4301 /**********************************************************************
4302 * LoadMenuIndirect (USER.220)
4304 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4307 WORD version
, offset
;
4308 LPCSTR p
= (LPCSTR
)template;
4310 TRACE("(%p)\n", template );
4311 version
= GET_WORD(p
);
4315 WARN("version must be 0 for Win16\n" );
4318 offset
= GET_WORD(p
);
4319 p
+= sizeof(WORD
) + offset
;
4320 if (!(hMenu
= CreateMenu())) return 0;
4321 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4323 DestroyMenu( hMenu
);
4326 return HMENU_16(hMenu
);
4330 /**********************************************************************
4331 * LoadMenuIndirectW (USER32.@)
4333 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4336 WORD version
, offset
;
4337 LPCSTR p
= (LPCSTR
)template;
4339 version
= GET_WORD(p
);
4341 TRACE("%p, ver %d\n", template, version
);
4344 case 0: /* standard format is version of 0 */
4345 offset
= GET_WORD(p
);
4346 p
+= sizeof(WORD
) + offset
;
4347 if (!(hMenu
= CreateMenu())) return 0;
4348 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4350 DestroyMenu( hMenu
);
4354 case 1: /* extended format is version of 1 */
4355 offset
= GET_WORD(p
);
4356 p
+= sizeof(WORD
) + offset
;
4357 if (!(hMenu
= CreateMenu())) return 0;
4358 if (!MENUEX_ParseResource( p
, hMenu
))
4360 DestroyMenu( hMenu
);
4365 ERR("version %d not supported.\n", version
);
4371 /**********************************************************************
4372 * LoadMenuIndirectA (USER32.@)
4374 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4376 return LoadMenuIndirectW( template );
4380 /**********************************************************************
4383 BOOL WINAPI
IsMenu(HMENU hmenu
)
4385 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4389 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4395 /**********************************************************************
4396 * GetMenuItemInfo_common
4399 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4400 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4402 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4404 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4409 if( lpmii
->fMask
& MIIM_TYPE
) {
4410 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4411 WARN("invalid combination of fMask bits used\n");
4412 /* this does not happen on Win9x/ME */
4413 SetLastError( ERROR_INVALID_PARAMETER
);
4416 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4417 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4418 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4419 if( lpmii
->fType
& MFT_BITMAP
) {
4420 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4422 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4423 /* this does not happen on Win9x/ME */
4424 lpmii
->dwTypeData
= 0;
4429 /* copy the text string */
4430 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4432 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4435 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4437 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4443 len
= strlenW(menu
->text
);
4444 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4445 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4449 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4450 0, NULL
, NULL
) - 1;
4451 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4452 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4453 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4454 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4456 /* if we've copied a substring we return its length */
4457 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4458 if (lpmii
->cch
<= len
+ 1)
4463 /* return length of string */
4464 /* not on Win9x/ME if fType & MFT_BITMAP */
4470 if (lpmii
->fMask
& MIIM_FTYPE
)
4471 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4473 if (lpmii
->fMask
& MIIM_BITMAP
)
4474 lpmii
->hbmpItem
= menu
->hbmpItem
;
4476 if (lpmii
->fMask
& MIIM_STATE
)
4477 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4479 if (lpmii
->fMask
& MIIM_ID
)
4480 lpmii
->wID
= menu
->wID
;
4482 if (lpmii
->fMask
& MIIM_SUBMENU
)
4483 lpmii
->hSubMenu
= menu
->hSubMenu
;
4485 /* hSubMenu is always cleared
4486 * (not on Win9x/ME ) */
4487 lpmii
->hSubMenu
= 0;
4490 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4491 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4492 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4494 if (lpmii
->fMask
& MIIM_DATA
)
4495 lpmii
->dwItemData
= menu
->dwItemData
;
4500 /**********************************************************************
4501 * GetMenuItemInfoA (USER32.@)
4503 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4504 LPMENUITEMINFOA lpmii
)
4508 if( lpmii
->cbSize
!= sizeof( mii
) &&
4509 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4510 SetLastError( ERROR_INVALID_PARAMETER
);
4513 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4514 mii
.cbSize
= sizeof( mii
);
4515 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4516 (LPMENUITEMINFOW
)&mii
, FALSE
);
4517 mii
.cbSize
= lpmii
->cbSize
;
4518 memcpy( lpmii
, &mii
, mii
.cbSize
);
4522 /**********************************************************************
4523 * GetMenuItemInfoW (USER32.@)
4525 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4526 LPMENUITEMINFOW 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
, &mii
, TRUE
);
4538 mii
.cbSize
= lpmii
->cbSize
;
4539 memcpy( lpmii
, &mii
, mii
.cbSize
);
4544 /* set a menu item text from a ASCII or Unicode string */
4545 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4551 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4552 strcpyW( menu
->text
, text
);
4556 LPCSTR str
= (LPCSTR
)text
;
4557 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4558 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4559 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4564 /**********************************************************************
4565 * SetMenuItemInfo_common
4568 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4569 const MENUITEMINFOW
*lpmii
,
4572 if (!menu
) return FALSE
;
4574 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4576 if (lpmii
->fMask
& MIIM_TYPE
) {
4577 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4578 WARN("invalid combination of fMask bits used\n");
4579 /* this does not happen on Win9x/ME */
4580 SetLastError( ERROR_INVALID_PARAMETER
);
4584 /* Remove the old type bits and replace them with the new ones */
4585 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4586 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4588 if (IS_STRING_ITEM(menu
->fType
)) {
4589 HeapFree(GetProcessHeap(), 0, menu
->text
);
4590 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4591 } else if( (menu
->fType
) & MFT_BITMAP
)
4592 menu
->hbmpItem
= HBITMAP_32(LOWORD(lpmii
->dwTypeData
));
4595 if (lpmii
->fMask
& MIIM_FTYPE
) {
4596 if(( lpmii
->fType
& MFT_BITMAP
)) {
4597 SetLastError( ERROR_INVALID_PARAMETER
);
4600 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4601 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4603 if (lpmii
->fMask
& MIIM_STRING
) {
4604 /* free the string when used */
4605 HeapFree(GetProcessHeap(), 0, menu
->text
);
4606 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4609 if (lpmii
->fMask
& MIIM_STATE
)
4611 /* Other menu items having MFS_DEFAULT are not converted
4613 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4616 if (lpmii
->fMask
& MIIM_ID
)
4617 menu
->wID
= lpmii
->wID
;
4619 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4620 menu
->hSubMenu
= lpmii
->hSubMenu
;
4621 if (menu
->hSubMenu
) {
4622 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4624 subMenu
->wFlags
|= MF_POPUP
;
4625 menu
->fType
|= MF_POPUP
;
4628 SetLastError( ERROR_INVALID_PARAMETER
);
4633 menu
->fType
&= ~MF_POPUP
;
4636 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4638 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4639 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4641 if (lpmii
->fMask
& MIIM_DATA
)
4642 menu
->dwItemData
= lpmii
->dwItemData
;
4644 if (lpmii
->fMask
& MIIM_BITMAP
)
4645 menu
->hbmpItem
= lpmii
->hbmpItem
;
4647 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4648 menu
->fType
|= MFT_SEPARATOR
;
4650 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4654 /**********************************************************************
4655 * SetMenuItemInfoA (USER32.@)
4657 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4658 const MENUITEMINFOA
*lpmii
)
4662 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4664 if( lpmii
->cbSize
!= sizeof( mii
) &&
4665 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4666 SetLastError( ERROR_INVALID_PARAMETER
);
4669 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4670 if( lpmii
->cbSize
!= sizeof( mii
)) {
4671 mii
.cbSize
= sizeof( mii
);
4672 mii
.hbmpItem
= NULL
;
4674 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4675 (const MENUITEMINFOW
*)&mii
, FALSE
);
4678 /**********************************************************************
4679 * SetMenuItemInfoW (USER32.@)
4681 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4682 const MENUITEMINFOW
*lpmii
)
4686 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4688 if( lpmii
->cbSize
!= sizeof( mii
) &&
4689 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4690 SetLastError( ERROR_INVALID_PARAMETER
);
4693 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4694 if( lpmii
->cbSize
!= sizeof( mii
)) {
4695 mii
.cbSize
= sizeof( mii
);
4696 mii
.hbmpItem
= NULL
;
4698 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4699 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4702 /**********************************************************************
4703 * SetMenuDefaultItem (USER32.@)
4706 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4712 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4714 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4716 /* reset all default-item flags */
4718 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4720 item
->fState
&= ~MFS_DEFAULT
;
4723 /* no default item */
4732 if ( uItem
>= menu
->nItems
) return FALSE
;
4733 item
[uItem
].fState
|= MFS_DEFAULT
;
4738 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4740 if (item
->wID
== uItem
)
4742 item
->fState
|= MFS_DEFAULT
;
4751 /**********************************************************************
4752 * GetMenuDefaultItem (USER32.@)
4754 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4760 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4762 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4764 /* find default item */
4768 if (! item
) return -1;
4770 while ( !( item
->fState
& MFS_DEFAULT
) )
4773 if (i
>= menu
->nItems
) return -1;
4776 /* default: don't return disabled items */
4777 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4779 /* search rekursiv when needed */
4780 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4783 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4784 if ( -1 != ret
) return ret
;
4786 /* when item not found in submenu, return the popup item */
4788 return ( bypos
) ? i
: item
->wID
;
4793 /**********************************************************************
4794 * InsertMenuItemA (USER32.@)
4796 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4797 const MENUITEMINFOA
*lpmii
)
4802 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4804 if( lpmii
->cbSize
!= sizeof( mii
) &&
4805 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4806 SetLastError( ERROR_INVALID_PARAMETER
);
4809 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4810 if( lpmii
->cbSize
!= sizeof( mii
)) {
4811 mii
.cbSize
= sizeof( mii
);
4812 mii
.hbmpItem
= NULL
;
4815 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4816 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4820 /**********************************************************************
4821 * InsertMenuItemW (USER32.@)
4823 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4824 const MENUITEMINFOW
*lpmii
)
4829 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4831 if( lpmii
->cbSize
!= sizeof( mii
) &&
4832 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4833 SetLastError( ERROR_INVALID_PARAMETER
);
4836 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4837 if( lpmii
->cbSize
!= sizeof( mii
)) {
4838 mii
.cbSize
= sizeof( mii
);
4839 mii
.hbmpItem
= NULL
;
4842 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4843 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4846 /**********************************************************************
4847 * CheckMenuRadioItem (USER32.@)
4850 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4851 UINT first
, UINT last
, UINT check
,
4856 MENUITEM
*mi_first
= NULL
, *mi_check
;
4857 HMENU m_first
, m_check
;
4859 for (i
= first
; i
<= last
; i
++)
4866 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4867 if (!mi_first
) continue;
4868 mi_check
= mi_first
;
4874 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4875 if (!mi_check
) continue;
4878 if (m_first
!= m_check
) continue;
4879 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4883 mi_check
->fType
|= MFT_RADIOCHECK
;
4884 mi_check
->fState
|= MFS_CHECKED
;
4889 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4890 mi_check
->fState
&= ~MFS_CHECKED
;
4898 /**********************************************************************
4899 * GetMenuItemRect (USER32.@)
4901 * ATTENTION: Here, the returned values in rect are the screen
4902 * coordinates of the item just like if the menu was
4903 * always on the upper left side of the application.
4906 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4909 POPUPMENU
*itemMenu
;
4913 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4915 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4916 referenceHwnd
= hwnd
;
4920 itemMenu
= MENU_GetMenu(hMenu
);
4921 if (itemMenu
== NULL
)
4924 if(itemMenu
->hWnd
== 0)
4926 referenceHwnd
= itemMenu
->hWnd
;
4929 if ((rect
== NULL
) || (item
== NULL
))
4934 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4940 /**********************************************************************
4941 * SetMenuInfo (USER32.@)
4944 * MIM_APPLYTOSUBMENUS
4945 * actually use the items to draw the menu
4947 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4951 TRACE("(%p %p)\n", hMenu
, lpmi
);
4953 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4956 if (lpmi
->fMask
& MIM_BACKGROUND
)
4957 menu
->hbrBack
= lpmi
->hbrBack
;
4959 if (lpmi
->fMask
& MIM_HELPID
)
4960 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4962 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4963 menu
->cyMax
= lpmi
->cyMax
;
4965 if (lpmi
->fMask
& MIM_MENUDATA
)
4966 menu
->dwMenuData
= lpmi
->dwMenuData
;
4968 if (lpmi
->fMask
& MIM_STYLE
)
4970 menu
->dwStyle
= lpmi
->dwStyle
;
4971 if (menu
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
4972 if (menu
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
4973 if (menu
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
4974 if (menu
->dwStyle
& MNS_NOTIFYBYPOS
) FIXME("MNS_NOTIFYBYPOS partially implemented\n");
4982 /**********************************************************************
4983 * GetMenuInfo (USER32.@)
4989 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
4992 TRACE("(%p %p)\n", hMenu
, lpmi
);
4994 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
4997 if (lpmi
->fMask
& MIM_BACKGROUND
)
4998 lpmi
->hbrBack
= menu
->hbrBack
;
5000 if (lpmi
->fMask
& MIM_HELPID
)
5001 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5003 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5004 lpmi
->cyMax
= menu
->cyMax
;
5006 if (lpmi
->fMask
& MIM_MENUDATA
)
5007 lpmi
->dwMenuData
= menu
->dwMenuData
;
5009 if (lpmi
->fMask
& MIM_STYLE
)
5010 lpmi
->dwStyle
= menu
->dwStyle
;
5018 /**********************************************************************
5019 * SetMenuContextHelpId (USER32.@)
5021 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5025 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5027 if ((menu
= MENU_GetMenu(hMenu
)))
5029 menu
->dwContextHelpID
= dwContextHelpID
;
5036 /**********************************************************************
5037 * GetMenuContextHelpId (USER32.@)
5039 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5043 TRACE("(%p)\n", hMenu
);
5045 if ((menu
= MENU_GetMenu(hMenu
)))
5047 return menu
->dwContextHelpID
;
5052 /**********************************************************************
5053 * MenuItemFromPoint (USER32.@)
5055 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5057 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5060 /*FIXME: Do we have to handle hWnd here? */
5061 if (!menu
) return -1;
5062 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5067 /**********************************************************************
5068 * translate_accelerator
5070 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5071 BYTE fVirt
, WORD key
, WORD cmd
)
5076 if (wParam
!= key
) return FALSE
;
5078 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5079 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5080 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5082 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5084 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5086 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5092 if(fVirt
& FVIRTKEY
)
5094 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5095 wParam
, 0xff & HIWORD(lParam
));
5097 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5098 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5102 if (!(lParam
& 0x01000000)) /* no special_key */
5104 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5105 { /* ^^ ALT pressed */
5106 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5115 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5119 HMENU hMenu
, hSubMenu
, hSysMenu
;
5120 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5122 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5123 hSysMenu
= get_win_sys_menu( hWnd
);
5125 /* find menu item and ask application to initialize it */
5126 /* 1. in the system menu */
5127 hSubMenu
= hSysMenu
;
5129 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5133 if (!IsWindowEnabled(hWnd
))
5137 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5138 if(hSubMenu
!= hSysMenu
)
5140 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5141 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5142 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5144 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5147 else /* 2. in the window's menu */
5151 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5155 if (!IsWindowEnabled(hWnd
))
5159 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5160 if(hSubMenu
!= hMenu
)
5162 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5163 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5164 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5166 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5173 if (uSysStat
!= (UINT
)-1)
5175 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5182 if (uStat
!= (UINT
)-1)
5188 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5200 if( mesg
==WM_COMMAND
)
5202 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5203 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5205 else if( mesg
==WM_SYSCOMMAND
)
5207 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5208 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5212 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5213 * #0: unknown (please report!)
5214 * #1: for WM_KEYUP,WM_SYSKEYUP
5215 * #2: mouse is captured
5216 * #3: window is disabled
5217 * #4: it's a disabled system menu option
5218 * #5: it's a menu option, but window is iconic
5219 * #6: it's a menu option, but disabled
5221 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5223 ERR_(accel
)(" unknown reason - please report!\n");
5228 /**********************************************************************
5229 * TranslateAcceleratorA (USER32.@)
5230 * TranslateAccelerator (USER32.@)
5232 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5235 LPACCEL16 lpAccelTbl
;
5239 if (!hWnd
|| !msg
) return 0;
5241 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5243 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5247 wParam
= msg
->wParam
;
5249 switch (msg
->message
)
5258 char ch
= LOWORD(wParam
);
5260 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5261 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5269 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5270 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5274 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5275 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5277 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5282 /**********************************************************************
5283 * TranslateAcceleratorW (USER32.@)
5285 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5288 LPACCEL16 lpAccelTbl
;
5291 if (!hWnd
|| !msg
) return 0;
5293 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5295 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5299 switch (msg
->message
)
5311 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5312 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5316 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5317 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5319 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);