4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
43 #include "wine/port.h"
54 #include "wine/winbase16.h"
55 #include "wine/winuser16.h"
57 #include "wine/server.h"
58 #include "wine/unicode.h"
59 #include "wine/exception.h"
62 #include "user_private.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
66 WINE_DECLARE_DEBUG_CHANNEL(accel
);
68 /* internal popup menu window messages */
70 #define MM_SETMENUHANDLE (WM_USER + 0)
71 #define MM_GETMENUHANDLE (WM_USER + 1)
73 /* Menu item structure */
75 /* ----------- MENUITEMINFO Stuff ----------- */
76 UINT fType
; /* Item type. */
77 UINT fState
; /* Item state. */
78 UINT_PTR wID
; /* Item id. */
79 HMENU hSubMenu
; /* Pop-up menu. */
80 HBITMAP hCheckBit
; /* Bitmap when checked. */
81 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
82 LPWSTR text
; /* Item text. */
83 ULONG_PTR dwItemData
; /* Application defined. */
84 LPWSTR dwTypeData
; /* depends on fMask */
85 HBITMAP hbmpItem
; /* bitmap */
86 /* ----------- Wine stuff ----------- */
87 RECT rect
; /* Item area (relative to menu window) */
88 UINT xTab
; /* X position of text after Tab */
89 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
93 /* Popup menu structure */
95 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
96 WORD wMagic
; /* Magic number */
97 WORD Width
; /* Width of the whole menu */
98 WORD Height
; /* Height of the whole menu */
99 UINT nItems
; /* Number of items in the menu */
100 HWND hWnd
; /* Window containing the menu */
101 MENUITEM
*items
; /* Array of menu items */
102 UINT FocusedItem
; /* Currently focused item */
103 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
104 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
105 BOOL bScrolling
; /* Scroll arrows are active */
106 UINT nScrollPos
; /* Current scroll position */
107 UINT nTotalHeight
; /* Total height of menu items inside menu */
108 /* ------------ MENUINFO members ------ */
109 DWORD dwStyle
; /* Extended menu style */
110 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
111 HBRUSH hbrBack
; /* brush for menu background */
112 DWORD dwContextHelpID
;
113 DWORD dwMenuData
; /* application defined value */
114 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
115 SIZE maxBmpSize
; /* Maximum size of the bitmap items */
116 } POPUPMENU
, *LPPOPUPMENU
;
118 /* internal flags for menu tracking */
120 #define TF_ENDMENU 0x10000
121 #define TF_SUSPENDPOPUP 0x20000
122 #define TF_SKIPREMOVE 0x40000
127 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
128 HMENU hTopMenu
; /* initial menu */
129 HWND hOwnerWnd
; /* where notifications are sent */
133 #define MENU_MAGIC 0x554d /* 'MU' */
138 /* Internal MENU_TrackMenu() flags */
139 #define TPM_INTERNAL 0xF0000000
140 #define TPM_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
);
422 MENUITEMINFOW miteminfo
;
423 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
424 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
425 /* decorate the menu with bitmaps */
426 minfo
.cbSize
= sizeof( MENUINFO
);
427 minfo
.dwStyle
= MNS_CHECKORBMP
;
428 minfo
.fMask
= MIM_STYLE
;
429 SetMenuInfo( hMenu
, &minfo
);
430 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
431 miteminfo
.fMask
= MIIM_BITMAP
;
432 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
433 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
434 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
435 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
436 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
437 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
438 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
439 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
440 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
443 ERR("Unable to load default system menu\n" );
445 TRACE("returning %p.\n", hMenu
);
451 /**********************************************************************
454 * Create a copy of the system menu. System menu in Windows is
455 * a special menu bar with the single entry - system menu popup.
456 * This popup is presented to the outside world as a "system menu".
457 * However, the real system menu handle is sometimes seen in the
458 * WM_MENUSELECT parameters (and Word 6 likes it this way).
460 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
464 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
465 if ((hMenu
= CreateMenu()))
467 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
468 menu
->wFlags
= MF_SYSMENU
;
469 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
470 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
473 hPopupMenu
= MENU_CopySysPopup();
477 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
478 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
480 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
481 (UINT_PTR
)hPopupMenu
, NULL
);
483 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
484 menu
->items
[0].fState
= 0;
485 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
487 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
490 DestroyMenu( hMenu
);
492 ERR("failed to load system menu!\n");
497 /***********************************************************************
498 * MENU_InitSysMenuPopup
500 * Grey the appropriate items in System menu.
502 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
506 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
507 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
508 gray
= ((style
& WS_MAXIMIZE
) != 0);
509 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
510 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
511 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
512 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
513 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
514 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
515 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
516 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
518 /* The menu item must keep its state if it's disabled */
520 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
524 /******************************************************************************
526 * UINT MENU_GetStartOfNextColumn(
529 *****************************************************************************/
531 static UINT
MENU_GetStartOfNextColumn(
534 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
538 return NO_SELECTED_ITEM
;
540 i
= menu
->FocusedItem
+ 1;
541 if( i
== NO_SELECTED_ITEM
)
544 for( ; i
< menu
->nItems
; ++i
) {
545 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
549 return NO_SELECTED_ITEM
;
553 /******************************************************************************
555 * UINT MENU_GetStartOfPrevColumn(
558 *****************************************************************************/
560 static UINT
MENU_GetStartOfPrevColumn(
563 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
567 return NO_SELECTED_ITEM
;
569 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
570 return NO_SELECTED_ITEM
;
572 /* Find the start of the column */
574 for(i
= menu
->FocusedItem
; i
!= 0 &&
575 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
579 return NO_SELECTED_ITEM
;
581 for(--i
; i
!= 0; --i
) {
582 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
586 TRACE("ret %d.\n", i
);
593 /***********************************************************************
596 * Find a menu item. Return a pointer on the item, and modifies *hmenu
597 * in case the item was in a sub-menu.
599 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
602 MENUITEM
*fallback
= NULL
;
603 UINT fallback_pos
= 0;
606 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
607 if (wFlags
& MF_BYPOSITION
)
609 if (*nPos
>= menu
->nItems
) return NULL
;
610 return &menu
->items
[*nPos
];
614 MENUITEM
*item
= menu
->items
;
615 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
617 if (item
->fType
& MF_POPUP
)
619 HMENU hsubmenu
= item
->hSubMenu
;
620 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
626 else if (item
->wID
== *nPos
)
628 /* fallback to this item if nothing else found */
633 else if (item
->wID
== *nPos
)
642 *nPos
= fallback_pos
;
647 /***********************************************************************
650 * Find a Sub menu. Return the position of the submenu, and modifies
651 * *hmenu in case it is found in another sub-menu.
652 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
654 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
659 if (((*hmenu
)==(HMENU
)0xffff) ||
660 (!(menu
= MENU_GetMenu(*hmenu
))))
661 return NO_SELECTED_ITEM
;
663 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
664 if(!(item
->fType
& MF_POPUP
)) continue;
665 if (item
->hSubMenu
== hSubTarget
) {
669 HMENU hsubmenu
= item
->hSubMenu
;
670 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
671 if (pos
!= NO_SELECTED_ITEM
) {
677 return NO_SELECTED_ITEM
;
680 /***********************************************************************
683 static void MENU_FreeItemData( MENUITEM
* item
)
686 HeapFree( GetProcessHeap(), 0, item
->text
);
689 /***********************************************************************
690 * MENU_AdjustMenuItemRect
692 * Adjust menu item rectangle according to scrolling state.
695 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
697 if (menu
->bScrolling
)
699 UINT arrow_bitmap_height
;
702 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
703 arrow_bitmap_height
= bmp
.bmHeight
;
704 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
705 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
710 /***********************************************************************
711 * MENU_FindItemByCoords
713 * Find the item at the specified coordinates (screen coords). Does
714 * not work for child windows and therefore should not be called for
715 * an arbitrary system menu.
717 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
718 POINT pt
, UINT
*pos
)
724 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
728 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
731 MENU_AdjustMenuItemRect(menu
, &rect
);
732 if (PtInRect(&rect
, pt
))
742 /***********************************************************************
745 * Find the menu item selected by a key press.
746 * Return item id, -1 if none, -2 if we should close the menu.
748 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
749 WCHAR key
, BOOL forceMenuChar
)
751 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
753 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
757 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
758 MENUITEM
*item
= menu
->items
;
765 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
769 WCHAR
*p
= item
->text
- 2;
772 p
= strchrW (p
+ 2, '&');
774 while (p
!= NULL
&& p
[1] == '&');
775 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
779 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
780 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
781 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
782 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
788 /***********************************************************************
789 * MENU_GetBitmapItemSize
791 * Get the size of a bitmap item.
793 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
797 HBITMAP bmp
= lpitem
->hbmpItem
;
799 size
->cx
= size
->cy
= 0;
801 /* check if there is a magic menu item associated with this item */
802 switch( (INT_PTR
) bmp
)
804 case (INT_PTR
)HBMMENU_CALLBACK
:
806 MEASUREITEMSTRUCT measItem
;
807 measItem
.CtlType
= ODT_MENU
;
809 measItem
.itemID
= lpitem
->wID
;
810 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
811 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
812 measItem
.itemData
= lpitem
->dwItemData
;
813 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
814 size
->cx
= measItem
.itemWidth
;
815 size
->cy
= measItem
.itemHeight
;
819 case (INT_PTR
)HBMMENU_SYSTEM
:
820 if (lpitem
->dwItemData
)
822 bmp
= (HBITMAP
)lpitem
->dwItemData
;
826 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
827 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
828 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
829 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
830 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
831 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
834 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
835 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
836 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
837 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
838 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
839 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
842 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
844 size
->cx
= bm
.bmWidth
;
845 size
->cy
= bm
.bmHeight
;
849 /***********************************************************************
850 * MENU_DrawBitmapItem
852 * Draw a bitmap item.
854 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
855 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
861 int w
= rect
->right
- rect
->left
;
862 int h
= rect
->bottom
- rect
->top
;
865 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
868 /* Check if there is a magic menu item associated with this item */
869 if (IS_MAGIC_BITMAP(hbmToDraw
))
875 switch((INT_PTR
)hbmToDraw
)
877 case (INT_PTR
)HBMMENU_SYSTEM
:
878 if (lpitem
->dwItemData
)
880 bmp
= (HBITMAP
)lpitem
->dwItemData
;
881 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
885 static HBITMAP hBmpSysMenu
;
887 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
889 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
890 /* only use right half of the bitmap */
891 bmp_xoffset
= bm
.bmWidth
/ 2;
892 bm
.bmWidth
-= bmp_xoffset
;
895 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
896 flags
= DFCS_CAPTIONRESTORE
;
898 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
899 flags
= DFCS_CAPTIONMIN
;
901 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
902 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
904 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
905 flags
= DFCS_CAPTIONCLOSE
;
907 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
908 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
910 case (INT_PTR
)HBMMENU_CALLBACK
:
912 DRAWITEMSTRUCT drawItem
;
913 drawItem
.CtlType
= ODT_MENU
;
915 drawItem
.itemID
= lpitem
->wID
;
916 drawItem
.itemAction
= odaction
;
917 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
918 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
919 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
920 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
921 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
922 drawItem
.hwndItem
= (HWND
)hmenu
;
924 drawItem
.itemData
= lpitem
->dwItemData
;
925 drawItem
.rcItem
= *rect
;
926 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
930 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
933 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
936 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
939 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
943 FIXME("Magic %p not implemented\n", hbmToDraw
);
948 /* draw the magic bitmaps using marlett font characters */
949 /* FIXME: fontsize and the position (x,y) could probably be better */
950 HFONT hfont
, hfontsav
;
951 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
952 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
953 { 'M','a','r','l','e','t','t',0 } };
954 logfont
.lfHeight
= min( h
, w
) - 5 ;
955 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
956 hfont
= CreateFontIndirectW( &logfont
);
957 hfontsav
= SelectObject(hdc
, hfont
);
958 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
959 SelectObject(hdc
, hfontsav
);
960 DeleteObject( hfont
);
965 InflateRect( &r
, -1, -1 );
966 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
967 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
972 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
975 hdcMem
= CreateCompatibleDC( hdc
);
976 SelectObject( hdcMem
, bmp
);
978 /* handle fontsize > bitmap_height */
979 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
981 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
982 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
983 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
984 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
989 /***********************************************************************
992 * Calculate the size of the menu item and store it in lpitem->rect.
994 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
995 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
998 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
999 UINT arrow_bitmap_width
;
1003 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1004 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1005 (menuBar
? " (MenuBar)" : ""));
1007 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1008 arrow_bitmap_width
= bm
.bmWidth
;
1010 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1011 if( !menucharsize
.cx
) {
1012 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1013 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1014 * but it is unlikely an application will depend on that */
1015 ODitemheight
= HIWORD( GetDialogBaseUnits());
1018 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1020 if (lpitem
->fType
& MF_OWNERDRAW
)
1022 MEASUREITEMSTRUCT mis
;
1023 mis
.CtlType
= ODT_MENU
;
1025 mis
.itemID
= lpitem
->wID
;
1026 mis
.itemData
= lpitem
->dwItemData
;
1027 mis
.itemHeight
= ODitemheight
;
1029 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1030 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1031 * width of a menufont character to the width of an owner-drawn menu.
1033 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1035 /* under at least win95 you seem to be given a standard
1036 height for the menu and the height value is ignored */
1037 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1039 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1041 TRACE("id=%04lx size=%dx%d\n",
1042 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1043 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1047 if (lpitem
->fType
& MF_SEPARATOR
)
1049 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1051 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1059 if (lpitem
->hbmpItem
) {
1062 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1063 /* Keep the size of the bitmap in callback mode to be able
1064 * to draw it correctly */
1065 lpitem
->bmpsize
= size
;
1066 lppop
->maxBmpSize
.cx
= max( lppop
->maxBmpSize
.cx
, size
.cx
);
1067 lppop
->maxBmpSize
.cy
= max( lppop
->maxBmpSize
.cy
, size
.cy
);
1068 lpitem
->rect
.right
+= size
.cx
+ 2;
1069 itemheight
= size
.cy
+ 2;
1071 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1072 lpitem
->rect
.right
+= check_bitmap_width
;
1073 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1074 lpitem
->xTab
= lpitem
->rect
.right
;
1075 lpitem
->rect
.right
+= arrow_bitmap_width
;
1076 } else if (lpitem
->hbmpItem
) { /* menuBar */
1079 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1080 lpitem
->bmpsize
= size
;
1081 lpitem
->rect
.right
+= size
.cx
;
1082 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1083 itemheight
= size
.cy
;
1086 /* it must be a text item - unless it's the system menu */
1087 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1088 HFONT hfontOld
= NULL
;
1089 RECT rc
= lpitem
->rect
;
1090 LONG txtheight
, txtwidth
;
1092 if ( lpitem
->fState
& MFS_DEFAULT
) {
1093 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1096 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1097 DT_SINGLELINE
|DT_CALCRECT
);
1098 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1099 itemheight
= max( max( itemheight
, txtheight
),
1100 GetSystemMetrics( SM_CYMENU
) - 1);
1101 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1103 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1106 int n
= (int)( p
- lpitem
->text
);
1107 /* Item contains a tab (only meaningful in popup menus) */
1108 /* get text size before the tab */
1109 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1110 DT_SINGLELINE
|DT_CALCRECT
);
1111 txtwidth
= rc
.right
- rc
.left
;
1112 p
+= 1; /* advance past the Tab */
1113 /* get text size after the tab */
1114 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1115 DT_SINGLELINE
|DT_CALCRECT
);
1116 lpitem
->xTab
+= txtwidth
;
1117 txtheight
= max( txtheight
, tmpheight
);
1118 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1119 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1121 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1122 DT_SINGLELINE
|DT_CALCRECT
);
1123 txtwidth
= rc
.right
- rc
.left
;
1124 lpitem
->xTab
+= txtwidth
;
1126 lpitem
->rect
.right
+= 2 + txtwidth
;
1127 itemheight
= max( itemheight
,
1128 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1130 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1131 } else if( menuBar
) {
1132 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1134 lpitem
->rect
.bottom
+= itemheight
;
1135 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1139 /***********************************************************************
1140 * MENU_GetMaxPopupHeight
1143 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1146 return lppop
->cyMax
;
1147 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1151 /***********************************************************************
1152 * MENU_PopupMenuCalcSize
1154 * Calculate the size of a popup menu.
1156 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1161 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1163 lppop
->Width
= lppop
->Height
= 0;
1164 if (lppop
->nItems
== 0) return;
1167 SelectObject( hdc
, get_menu_font(FALSE
));
1172 lppop
->maxBmpSize
.cx
= 0;
1173 lppop
->maxBmpSize
.cy
= 0;
1175 while (start
< lppop
->nItems
)
1177 lpitem
= &lppop
->items
[start
];
1179 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1180 orgX
+= MENU_COL_SPACE
;
1181 orgY
= MENU_TOP_MARGIN
;
1183 maxTab
= maxTabWidth
= 0;
1184 /* Parse items until column break or end of menu */
1185 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1188 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1190 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1191 maxX
= max( maxX
, lpitem
->rect
.right
);
1192 orgY
= lpitem
->rect
.bottom
;
1193 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1195 maxTab
= max( maxTab
, lpitem
->xTab
);
1196 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1200 /* Finish the column (set all items to the largest width found) */
1201 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1202 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1204 lpitem
->rect
.right
= maxX
;
1205 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1206 lpitem
->xTab
= maxTab
;
1209 lppop
->Height
= max( lppop
->Height
, orgY
);
1212 lppop
->Width
= maxX
;
1214 /* space for 3d border */
1215 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1218 /* Adjust popup height if it exceeds maximum */
1219 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1220 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1221 if (lppop
->Height
>= maxHeight
)
1223 lppop
->Height
= maxHeight
;
1224 lppop
->bScrolling
= TRUE
;
1228 lppop
->bScrolling
= FALSE
;
1231 ReleaseDC( 0, hdc
);
1235 /***********************************************************************
1236 * MENU_MenuBarCalcSize
1238 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1239 * height is off by 1 pixel which causes lengthy window relocations when
1240 * active document window is maximized/restored.
1242 * Calculate the size of the menu bar.
1244 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1245 LPPOPUPMENU lppop
, HWND hwndOwner
)
1248 UINT start
, i
, helpPos
;
1249 int orgX
, orgY
, maxY
;
1251 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1252 if (lppop
->nItems
== 0) return;
1253 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1254 lppop
->Width
= lprect
->right
- lprect
->left
;
1256 maxY
= lprect
->top
+1;
1259 lppop
->maxBmpSize
.cx
= 0;
1260 lppop
->maxBmpSize
.cy
= 0;
1261 while (start
< lppop
->nItems
)
1263 lpitem
= &lppop
->items
[start
];
1264 orgX
= lprect
->left
;
1267 /* Parse items until line break or end of menu */
1268 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1270 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1272 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1274 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1275 debug_print_menuitem (" item: ", lpitem
, "");
1276 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1278 if (lpitem
->rect
.right
> lprect
->right
)
1280 if (i
!= start
) break;
1281 else lpitem
->rect
.right
= lprect
->right
;
1283 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1284 orgX
= lpitem
->rect
.right
;
1287 /* Finish the line (set all items to the largest height found) */
1288 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1291 lprect
->bottom
= maxY
;
1292 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1294 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1295 /* the last item (if several lines, only move the last line) */
1296 if (helpPos
== ~0U) return;
1297 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1298 orgY
= lpitem
->rect
.top
;
1299 orgX
= lprect
->right
;
1300 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1301 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1302 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1303 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1304 lpitem
->rect
.right
= orgX
;
1305 orgX
= lpitem
->rect
.left
;
1310 /***********************************************************************
1311 * MENU_DrawScrollArrows
1313 * Draw scroll arrows.
1316 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1318 HDC hdcMem
= CreateCompatibleDC(hdc
);
1319 HBITMAP hOrigBitmap
;
1320 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1324 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1325 arrow_bitmap_width
= bmp
.bmWidth
;
1326 arrow_bitmap_height
= bmp
.bmHeight
;
1329 if (lppop
->nScrollPos
)
1330 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1332 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1335 rect
.right
= lppop
->Width
;
1336 rect
.bottom
= arrow_bitmap_height
;
1337 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1338 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1339 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1340 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1341 rect
.bottom
= lppop
->Height
;
1342 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1343 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1344 SelectObject(hdcMem
, get_down_arrow_bitmap());
1346 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1347 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1348 lppop
->Height
- arrow_bitmap_height
,
1349 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1350 SelectObject(hdcMem
, hOrigBitmap
);
1355 /***********************************************************************
1358 * Draws the popup-menu arrow.
1360 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1361 UINT arrow_bitmap_height
)
1363 HDC hdcMem
= CreateCompatibleDC( hdc
);
1364 HBITMAP hOrigBitmap
;
1366 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1367 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1368 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1369 arrow_bitmap_width
, arrow_bitmap_height
,
1370 hdcMem
, 0, 0, SRCCOPY
);
1371 SelectObject( hdcMem
, hOrigBitmap
);
1374 /***********************************************************************
1377 * Draw a single menu item.
1379 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1380 UINT height
, BOOL menuBar
, UINT odaction
)
1383 BOOL flat_menu
= FALSE
;
1385 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1386 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1389 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1393 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1394 arrow_bitmap_width
= bmp
.bmWidth
;
1395 arrow_bitmap_height
= bmp
.bmHeight
;
1398 if (lpitem
->fType
& MF_SYSMENU
)
1400 if( !IsIconic(hwnd
) )
1401 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1405 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1406 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1410 if (lpitem
->fState
& MF_HILITE
)
1412 if(menuBar
&& !flat_menu
) {
1413 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1414 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1416 if(lpitem
->fState
& MF_GRAYED
)
1417 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1419 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1420 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1425 if (lpitem
->fState
& MF_GRAYED
)
1426 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1428 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1429 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1432 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1433 rect
= lpitem
->rect
;
1434 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1436 if (lpitem
->fType
& MF_OWNERDRAW
)
1439 ** Experimentation under Windows reveals that an owner-drawn
1440 ** menu is given the rectangle which includes the space it requested
1441 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1442 ** and a popup-menu arrow. This is the value of lpitem->rect.
1443 ** Windows will leave all drawing to the application except for
1444 ** the popup-menu arrow. Windows always draws that itself, after
1445 ** the menu owner has finished drawing.
1449 dis
.CtlType
= ODT_MENU
;
1451 dis
.itemID
= lpitem
->wID
;
1452 dis
.itemData
= lpitem
->dwItemData
;
1454 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1455 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1456 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1457 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1458 dis
.hwndItem
= (HWND
)hmenu
;
1461 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1462 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1463 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1464 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1465 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1466 /* Draw the popup-menu arrow */
1467 if (lpitem
->fType
& MF_POPUP
)
1468 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1469 arrow_bitmap_height
);
1473 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1475 if (lpitem
->fState
& MF_HILITE
)
1479 InflateRect (&rect
, -1, -1);
1480 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1481 InflateRect (&rect
, 1, 1);
1482 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1487 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1489 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1493 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1495 SetBkMode( hdc
, TRANSPARENT
);
1497 /* vertical separator */
1498 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1503 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1505 rc
.bottom
= height
- 3;
1508 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1509 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1510 LineTo( hdc
, rc
.left
, rc
.bottom
);
1511 SelectObject( hdc
, oldPen
);
1514 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1517 /* horizontal separator */
1518 if (lpitem
->fType
& MF_SEPARATOR
)
1525 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1528 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1529 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1530 LineTo( hdc
, rc
.right
, rc
.top
);
1531 SelectObject( hdc
, oldPen
);
1534 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1538 /* helper lines for debugging */
1539 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1540 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1541 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1542 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1545 if (lpitem
->hbmpItem
) {
1546 /* calculate the bitmap rectangle in coordinates relative
1547 * to the item rectangle */
1549 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1552 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1554 else if (menu
->dwStyle
& MNS_NOCHECK
)
1556 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1559 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1560 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1561 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1564 bmprc
.top
= (rect
.bottom
- rect
.top
-
1565 lpitem
->bmpsize
.cy
) / 2;
1566 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1572 INT y
= rect
.top
+ rect
.bottom
;
1574 int checked
= FALSE
;
1575 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1576 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1577 /* Draw the check mark
1580 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1582 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1583 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1584 lpitem
->hUnCheckBit
;
1585 if (bm
) /* we have a custom bitmap */
1587 HDC hdcMem
= CreateCompatibleDC( hdc
);
1589 SelectObject( hdcMem
, bm
);
1590 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1591 check_bitmap_width
, check_bitmap_height
,
1592 hdcMem
, 0, 0, SRCCOPY
);
1596 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1599 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1600 check_bitmap_height
, 1, 1, NULL
);
1601 HDC hdcMem
= CreateCompatibleDC( hdc
);
1603 SelectObject( hdcMem
, bm
);
1604 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1605 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1606 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1607 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1608 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1609 hdcMem
, 0, 0, SRCCOPY
);
1615 if( lpitem
->hbmpItem
&&
1616 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1618 /* some applications make this assumption on the DC's origin */
1619 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1620 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1622 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1624 /* Draw the popup-menu arrow */
1625 if (lpitem
->fType
& MF_POPUP
)
1626 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1627 arrow_bitmap_height
);
1629 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1630 rect
.left
+= check_bitmap_width
;
1631 rect
.right
-= arrow_bitmap_width
;
1633 else if( lpitem
->hbmpItem
)
1634 { /* Draw the bitmap */
1637 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1638 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1640 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1642 /* process text if present */
1648 UINT uFormat
= (menuBar
) ?
1649 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1650 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1652 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1653 rect
.left
+= menu
->maxBmpSize
.cx
;
1655 if ( lpitem
->fState
& MFS_DEFAULT
)
1657 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1661 if( lpitem
->hbmpItem
)
1662 rect
.left
+= lpitem
->bmpsize
.cx
;
1663 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1664 rect
.left
+= menucharsize
.cx
;
1665 rect
.right
-= menucharsize
.cx
;
1668 for (i
= 0; lpitem
->text
[i
]; i
++)
1669 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1672 if(lpitem
->fState
& MF_GRAYED
)
1674 if (!(lpitem
->fState
& MF_HILITE
) )
1676 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1677 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1678 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1679 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1681 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1684 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1686 /* paint the shortcut text */
1687 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1689 if (lpitem
->text
[i
] == '\t')
1691 rect
.left
= lpitem
->xTab
;
1692 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1696 rect
.right
= lpitem
->xTab
;
1697 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1700 if(lpitem
->fState
& MF_GRAYED
)
1702 if (!(lpitem
->fState
& MF_HILITE
) )
1704 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1705 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1706 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1707 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1709 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1711 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1715 SelectObject (hdc
, hfontOld
);
1720 /***********************************************************************
1721 * MENU_DrawPopupMenu
1723 * Paint a popup menu.
1725 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1727 HBRUSH hPrevBrush
= 0;
1730 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1732 GetClientRect( hwnd
, &rect
);
1734 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1735 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1739 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1741 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1745 BOOL flat_menu
= FALSE
;
1747 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1749 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1751 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1753 if( (menu
= MENU_GetMenu( hmenu
)))
1755 /* draw menu items */
1762 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1763 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1764 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1766 /* draw scroll arrows */
1767 if (menu
->bScrolling
)
1768 MENU_DrawScrollArrows(menu
, hdc
);
1772 SelectObject( hdc
, hPrevBrush
);
1777 /***********************************************************************
1780 * Paint a menu bar. Returns the height of the menu bar.
1781 * called from [windows/nonclient.c]
1783 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1788 HMENU hMenu
= GetMenu(hwnd
);
1790 lppop
= MENU_GetMenu( hMenu
);
1791 if (lppop
== NULL
|| lprect
== NULL
)
1793 return GetSystemMetrics(SM_CYMENU
);
1798 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1800 if (lppop
->Height
== 0)
1801 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1803 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1805 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1806 return lppop
->Height
;
1809 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1813 /***********************************************************************
1816 * Display a popup menu.
1818 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1819 INT x
, INT y
, INT xanchor
, INT yanchor
)
1827 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1828 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1830 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1831 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1833 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1834 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1837 /* store the owner for DrawItem */
1838 menu
->hwndOwner
= hwndOwner
;
1840 menu
->nScrollPos
= 0;
1841 MENU_PopupMenuCalcSize( menu
);
1843 /* adjust popup menu pos so that it fits within the desktop */
1845 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1846 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1848 /* FIXME: should use item rect */
1851 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1852 info
.cbSize
= sizeof(info
);
1853 GetMonitorInfoW( monitor
, &info
);
1855 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1856 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1858 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1859 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1861 if( x
+ width
> info
.rcWork
.right
)
1863 if( xanchor
&& x
>= width
- xanchor
)
1864 x
-= width
- xanchor
;
1866 if( x
+ width
> info
.rcWork
.right
)
1867 x
= info
.rcWork
.right
- width
;
1869 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1871 if( y
+ height
> info
.rcWork
.bottom
)
1873 if( yanchor
&& y
>= height
+ yanchor
)
1874 y
-= height
+ yanchor
;
1876 if( y
+ height
> info
.rcWork
.bottom
)
1877 y
= info
.rcWork
.bottom
- height
;
1879 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1881 /* NOTE: In Windows, top menu popup is not owned. */
1882 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1883 WS_POPUP
, x
, y
, width
, height
,
1884 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1886 if( !menu
->hWnd
) return FALSE
;
1887 if (!top_popup
) top_popup
= menu
->hWnd
;
1889 /* Display the window */
1891 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1892 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1893 UpdateWindow( menu
->hWnd
);
1898 /***********************************************************************
1899 * MENU_EnsureMenuItemVisible
1902 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1904 if (lppop
->bScrolling
)
1906 MENUITEM
*item
= &lppop
->items
[wIndex
];
1907 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1908 UINT nOldPos
= lppop
->nScrollPos
;
1910 UINT arrow_bitmap_height
;
1913 GetClientRect(lppop
->hWnd
, &rc
);
1915 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1916 arrow_bitmap_height
= bmp
.bmHeight
;
1918 rc
.top
+= arrow_bitmap_height
;
1919 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1921 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1922 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1925 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1926 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1927 MENU_DrawScrollArrows(lppop
, hdc
);
1929 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1931 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1932 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1933 MENU_DrawScrollArrows(lppop
, hdc
);
1939 /***********************************************************************
1942 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1943 BOOL sendMenuSelect
, HMENU topmenu
)
1948 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1950 lppop
= MENU_GetMenu( hmenu
);
1951 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1953 if (lppop
->FocusedItem
== wIndex
) return;
1954 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1955 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1956 if (!top_popup
) top_popup
= lppop
->hWnd
;
1958 SelectObject( hdc
, get_menu_font(FALSE
));
1960 /* Clear previous highlighted item */
1961 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1963 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1964 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1965 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1969 /* Highlight new item (if any) */
1970 lppop
->FocusedItem
= wIndex
;
1971 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1973 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1974 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1975 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1976 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1977 &lppop
->items
[wIndex
], lppop
->Height
,
1978 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1982 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1983 SendMessageW( hwndOwner
, WM_MENUSELECT
,
1984 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1985 ip
->fType
| ip
->fState
|
1986 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1989 else if (sendMenuSelect
) {
1992 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1993 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1994 MENUITEM
*ip
= &ptm
->items
[pos
];
1995 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
1996 ip
->fType
| ip
->fState
|
1997 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2001 ReleaseDC( lppop
->hWnd
, hdc
);
2005 /***********************************************************************
2006 * MENU_MoveSelection
2008 * Moves currently selected item according to the offset parameter.
2009 * If there is no selection then it should select the last item if
2010 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2012 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2017 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2019 menu
= MENU_GetMenu( hmenu
);
2020 if ((!menu
) || (!menu
->items
)) return;
2022 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2024 if( menu
->nItems
== 1 ) return; else
2025 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2027 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2029 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2034 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2035 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2036 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2038 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2044 /**********************************************************************
2047 * Set an item's flags, id and text ptr. Called by InsertMenu() and
2050 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
2053 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
2054 TRACE("flags=%x str=%p\n", flags
, str
);
2056 if (IS_STRING_ITEM(flags
))
2058 LPWSTR prevText
= item
->text
;
2061 flags
|= MF_SEPARATOR
;
2067 /* Item beginning with a backspace is a help item */
2073 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
2075 strcpyW( text
, str
);
2078 item
->hbmpItem
= NULL
;
2079 HeapFree( GetProcessHeap(), 0, prevText
);
2081 else if(( flags
& MFT_BITMAP
)) {
2082 item
->hbmpItem
= HBITMAP_32(LOWORD(str
));
2083 /* setting bitmap clears text */
2084 HeapFree( GetProcessHeap(), 0, item
->text
);
2088 if (flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
2090 if (flags
& MF_OWNERDRAW
)
2091 item
->dwItemData
= (DWORD_PTR
)str
;
2093 item
->dwItemData
= 0;
2095 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
2096 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
2098 if (flags
& MF_POPUP
)
2100 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
2101 if (menu
) menu
->wFlags
|= MF_POPUP
;
2113 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
2115 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
2116 flags
|= MF_POPUP
; /* keep popup */
2118 item
->fType
= flags
& TYPE_MASK
;
2119 /* MFS_DEFAULT is not accepted. MF_HILITE is not listed as a valid flag
2120 for ModifyMenu, but Windows accepts it */
2121 item
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
2123 /* Don't call SetRectEmpty here! */
2125 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
2130 /**********************************************************************
2133 * Insert (allocate) a new item into a menu.
2135 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2140 if (!(menu
= MENU_GetMenu(hMenu
)))
2143 /* Find where to insert new item */
2145 if (flags
& MF_BYPOSITION
) {
2146 if (pos
> menu
->nItems
)
2149 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2152 if (!(menu
= MENU_GetMenu( hMenu
)))
2157 /* Make sure that MDI system buttons stay on the right side.
2158 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2159 * regardless of their id.
2161 while (pos
> 0 && (menu
->items
[pos
- 1].fType
& MFT_BITMAP
) &&
2162 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2163 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2166 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2168 /* Create new items array */
2170 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2173 WARN("allocation failed\n" );
2176 if (menu
->nItems
> 0)
2178 /* Copy the old array into the new one */
2179 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2180 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2181 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2182 HeapFree( GetProcessHeap(), 0, menu
->items
);
2184 menu
->items
= newItems
;
2186 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2187 menu
->Height
= 0; /* force size recalculate */
2188 return &newItems
[pos
];
2192 /**********************************************************************
2193 * MENU_ParseResource
2195 * Parse a standard menu resource and add items to the menu.
2196 * Return a pointer to the end of the resource.
2198 * NOTE: flags is equivalent to the mtOption field
2200 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
2208 flags
= GET_WORD(res
);
2209 end_flag
= flags
& MF_END
;
2210 /* Remove MF_END because it has the same value as MF_HILITE */
2212 res
+= sizeof(WORD
);
2213 if (!(flags
& MF_POPUP
))
2216 res
+= sizeof(WORD
);
2219 if (!unicode
) res
+= strlen(str
) + 1;
2220 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
2221 if (flags
& MF_POPUP
)
2223 HMENU hSubMenu
= CreatePopupMenu();
2224 if (!hSubMenu
) return NULL
;
2225 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
2227 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2228 else AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, (LPCWSTR
)str
);
2230 else /* Not a popup */
2232 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
2233 else AppendMenuW( hMenu
, flags
, id
,
2234 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
2236 } while (!end_flag
);
2241 /**********************************************************************
2242 * MENUEX_ParseResource
2244 * Parse an extended menu resource and add items to the menu.
2245 * Return a pointer to the end of the resource.
2247 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2253 mii
.cbSize
= sizeof(mii
);
2254 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2255 mii
.fType
= GET_DWORD(res
);
2256 res
+= sizeof(DWORD
);
2257 mii
.fState
= GET_DWORD(res
);
2258 res
+= sizeof(DWORD
);
2259 mii
.wID
= GET_DWORD(res
);
2260 res
+= sizeof(DWORD
);
2261 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2262 res
+= sizeof(WORD
);
2263 /* Align the text on a word boundary. */
2264 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2265 mii
.dwTypeData
= (LPWSTR
) res
;
2266 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2267 /* Align the following fields on a dword boundary. */
2268 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2270 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2271 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2273 if (resinfo
& 1) { /* Pop-up? */
2274 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2275 res
+= sizeof(DWORD
);
2276 mii
.hSubMenu
= CreatePopupMenu();
2279 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2280 DestroyMenu(mii
.hSubMenu
);
2283 mii
.fMask
|= MIIM_SUBMENU
;
2284 mii
.fType
|= MF_POPUP
;
2286 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2288 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2289 mii
.wID
, mii
.fType
);
2290 mii
.fType
|= MF_SEPARATOR
;
2292 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2293 } while (!(resinfo
& MF_END
));
2298 /***********************************************************************
2301 * Return the handle of the selected sub-popup menu (if any).
2303 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2308 menu
= MENU_GetMenu( hmenu
);
2310 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2312 item
= &menu
->items
[menu
->FocusedItem
];
2313 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2314 return item
->hSubMenu
;
2319 /***********************************************************************
2320 * MENU_HideSubPopups
2322 * Hide the sub-popup menus of this menu.
2324 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2325 BOOL sendMenuSelect
, UINT wFlags
)
2327 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2329 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2331 if (menu
&& top_popup
)
2337 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2339 item
= &menu
->items
[menu
->FocusedItem
];
2340 if (!(item
->fType
& MF_POPUP
) ||
2341 !(item
->fState
& MF_MOUSESELECT
)) return;
2342 item
->fState
&= ~MF_MOUSESELECT
;
2343 hsubmenu
= item
->hSubMenu
;
2346 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2347 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2348 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2349 DestroyWindow( submenu
->hWnd
);
2352 if (!(wFlags
& TPM_NONOTIFY
))
2353 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2354 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2359 /***********************************************************************
2362 * Display the sub-menu of the selected item of this menu.
2363 * Return the handle of the submenu, or hmenu if no submenu to display.
2365 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2366 BOOL selectFirst
, UINT wFlags
)
2373 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2375 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2377 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2379 item
= &menu
->items
[menu
->FocusedItem
];
2380 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2383 /* message must be sent before using item,
2384 because nearly everything may be changed by the application ! */
2386 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2387 if (!(wFlags
& TPM_NONOTIFY
))
2388 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2389 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2391 item
= &menu
->items
[menu
->FocusedItem
];
2394 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2395 if (!(item
->fState
& MF_HILITE
))
2397 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2398 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2400 SelectObject( hdc
, get_menu_font(FALSE
));
2402 item
->fState
|= MF_HILITE
;
2403 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2404 ReleaseDC( menu
->hWnd
, hdc
);
2406 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2409 item
->fState
|= MF_MOUSESELECT
;
2411 if (IS_SYSTEM_MENU(menu
))
2413 MENU_InitSysMenuPopup(item
->hSubMenu
,
2414 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2415 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2417 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2418 rect
.top
= rect
.bottom
;
2419 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2420 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2424 GetWindowRect( menu
->hWnd
, &rect
);
2425 if (menu
->wFlags
& MF_POPUP
)
2427 RECT rc
= item
->rect
;
2429 MENU_AdjustMenuItemRect(menu
, &rc
);
2431 /* The first item in the popup menu has to be at the
2432 same y position as the focused menu item */
2433 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2434 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2435 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2436 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2437 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2441 rect
.left
+= item
->rect
.left
;
2442 rect
.top
+= item
->rect
.bottom
;
2443 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2444 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2448 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2449 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2451 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2452 return item
->hSubMenu
;
2457 /**********************************************************************
2460 HWND
MENU_IsMenuActive(void)
2465 /***********************************************************************
2468 * Walks menu chain trying to find a menu pt maps to.
2470 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2472 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2473 UINT item
= menu
->FocusedItem
;
2476 /* try subpopup first (if any) */
2477 ret
= (item
!= NO_SELECTED_ITEM
&&
2478 (menu
->items
[item
].fType
& MF_POPUP
) &&
2479 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2480 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2482 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2484 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2485 if( menu
->wFlags
& MF_POPUP
)
2487 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2489 else if (ht
== HTSYSMENU
)
2490 ret
= get_win_sys_menu( menu
->hWnd
);
2491 else if (ht
== HTMENU
)
2492 ret
= GetMenu( menu
->hWnd
);
2497 /***********************************************************************
2498 * MENU_ExecFocusedItem
2500 * Execute a menu item (for instance when user pressed Enter).
2501 * Return the wID of the executed item. Otherwise, -1 indicating
2502 * that no menu item was executed, -2 if a popup is shown;
2503 * Have to receive the flags for the TrackPopupMenu options to avoid
2504 * sending unwanted message.
2507 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2510 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2512 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2514 if (!menu
|| !menu
->nItems
||
2515 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2517 item
= &menu
->items
[menu
->FocusedItem
];
2519 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2521 if (!(item
->fType
& MF_POPUP
))
2523 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2525 /* If TPM_RETURNCMD is set you return the id, but
2526 do not send a message to the owner */
2527 if(!(wFlags
& TPM_RETURNCMD
))
2529 if( menu
->wFlags
& MF_SYSMENU
)
2530 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2531 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2534 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2535 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2537 if (dwStyle
& MNS_NOTIFYBYPOS
)
2538 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2541 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2549 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2556 /***********************************************************************
2557 * MENU_SwitchTracking
2559 * Helper function for menu navigation routines.
2561 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2563 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2564 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2566 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2568 if( pmt
->hTopMenu
!= hPtMenu
&&
2569 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2571 /* both are top level menus (system and menu-bar) */
2572 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2573 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2574 pmt
->hTopMenu
= hPtMenu
;
2576 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2577 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2581 /***********************************************************************
2584 * Return TRUE if we can go on with menu tracking.
2586 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2588 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2593 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2596 if( IS_SYSTEM_MENU(ptmenu
) )
2597 item
= ptmenu
->items
;
2599 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2603 if( ptmenu
->FocusedItem
!= id
)
2604 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2606 /* If the popup menu is not already "popped" */
2607 if(!(item
->fState
& MF_MOUSESELECT
))
2609 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2614 /* Else the click was on the menu bar, finish the tracking */
2619 /***********************************************************************
2622 * Return the value of MENU_ExecFocusedItem if
2623 * the selected item was not a popup. Else open the popup.
2624 * A -1 return value indicates that we go on with menu tracking.
2627 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2629 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2634 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2637 if( IS_SYSTEM_MENU(ptmenu
) )
2638 item
= ptmenu
->items
;
2640 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2642 if( item
&& (ptmenu
->FocusedItem
== id
))
2644 debug_print_menuitem ("FocusedItem: ", item
, "");
2646 if( !(item
->fType
& MF_POPUP
) )
2648 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2649 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2650 return executedMenuId
;
2653 /* If we are dealing with the top-level menu */
2654 /* and this is a click on an already "popped" item: */
2655 /* Stop the menu tracking and close the opened submenus */
2656 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2659 ptmenu
->bTimeToHide
= TRUE
;
2665 /***********************************************************************
2668 * Return TRUE if we can go on with menu tracking.
2670 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2672 UINT id
= NO_SELECTED_ITEM
;
2673 POPUPMENU
*ptmenu
= NULL
;
2677 ptmenu
= MENU_GetMenu( hPtMenu
);
2678 if( IS_SYSTEM_MENU(ptmenu
) )
2681 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2684 if( id
== NO_SELECTED_ITEM
)
2686 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2687 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2690 else if( ptmenu
->FocusedItem
!= id
)
2692 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2693 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2699 /***********************************************************************
2702 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2704 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2706 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2709 /* When skipping left, we need to do something special after the
2711 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2715 /* When skipping right, for the non-system menu, we need to
2716 handle the last non-special menu item (ie skip any window
2717 icons such as MDI maximize, restore or close) */
2718 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2720 UINT i
= menu
->FocusedItem
+ 1;
2721 while (i
< menu
->nItems
) {
2722 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2723 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2727 if (i
== menu
->nItems
) {
2731 /* When skipping right, we need to cater for the system menu */
2732 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2734 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2741 MDINEXTMENU next_menu
;
2746 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2747 next_menu
.hmenuNext
= 0;
2748 next_menu
.hwndNext
= 0;
2749 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2751 TRACE("%p [%p] -> %p [%p]\n",
2752 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2754 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2756 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2757 hNewWnd
= pmt
->hOwnerWnd
;
2758 if( IS_SYSTEM_MENU(menu
) )
2760 /* switch to the menu bar */
2762 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2766 menu
= MENU_GetMenu( hNewMenu
);
2767 id
= menu
->nItems
- 1;
2769 /* Skip backwards over any system predefined icons,
2770 eg. MDI close, restore etc icons */
2772 (menu
->items
[id
].wID
>= SC_SIZE
&&
2773 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2776 else if (style
& WS_SYSMENU
)
2778 /* switch to the system menu */
2779 hNewMenu
= get_win_sys_menu( hNewWnd
);
2783 else /* application returned a new menu to switch to */
2785 hNewMenu
= next_menu
.hmenuNext
;
2786 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2788 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2790 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2792 if (style
& WS_SYSMENU
&&
2793 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2795 /* get the real system menu */
2796 hNewMenu
= get_win_sys_menu(hNewWnd
);
2798 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2800 /* FIXME: Not sure what to do here;
2801 * perhaps try to track hNewMenu as a popup? */
2803 TRACE(" -- got confused.\n");
2810 if( hNewMenu
!= pmt
->hTopMenu
)
2812 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2814 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2815 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2818 if( hNewWnd
!= pmt
->hOwnerWnd
)
2820 pmt
->hOwnerWnd
= hNewWnd
;
2821 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2824 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2825 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2832 /***********************************************************************
2835 * The idea is not to show the popup if the next input message is
2836 * going to hide it anyway.
2838 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2842 msg
.hwnd
= pmt
->hOwnerWnd
;
2844 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2845 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2850 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2851 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2853 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2854 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2855 if( msg
.message
== WM_KEYDOWN
&&
2856 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2858 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2865 /* failures go through this */
2866 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2870 /***********************************************************************
2873 * Handle a VK_ESCAPE key event in a menu.
2875 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2877 BOOL bEndMenu
= TRUE
;
2879 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2881 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2883 if (menu
->wFlags
& MF_POPUP
)
2885 HMENU hmenutmp
, hmenuprev
;
2887 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2889 /* close topmost popup */
2890 while (hmenutmp
!= pmt
->hCurrentMenu
)
2892 hmenuprev
= hmenutmp
;
2893 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2896 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2897 pmt
->hCurrentMenu
= hmenuprev
;
2905 /***********************************************************************
2908 * Handle a VK_LEFT key event in a menu.
2910 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2913 HMENU hmenutmp
, hmenuprev
;
2916 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2917 menu
= MENU_GetMenu( hmenutmp
);
2919 /* Try to move 1 column left (if possible) */
2920 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2921 NO_SELECTED_ITEM
) {
2923 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2928 /* close topmost popup */
2929 while (hmenutmp
!= pmt
->hCurrentMenu
)
2931 hmenuprev
= hmenutmp
;
2932 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2935 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2936 pmt
->hCurrentMenu
= hmenuprev
;
2938 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2940 /* move menu bar selection if no more popups are left */
2942 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2943 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2945 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2947 /* A sublevel menu was displayed - display the next one
2948 * unless there is another displacement coming up */
2950 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2951 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2952 pmt
->hTopMenu
, TRUE
, wFlags
);
2958 /***********************************************************************
2961 * Handle a VK_RIGHT key event in a menu.
2963 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2966 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2969 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2971 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2972 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2974 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2976 /* If already displaying a popup, try to display sub-popup */
2978 hmenutmp
= pmt
->hCurrentMenu
;
2979 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2981 /* if subpopup was displayed then we are done */
2982 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2985 /* Check to see if there's another column */
2986 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2987 NO_SELECTED_ITEM
) {
2988 TRACE("Going to %d.\n", nextcol
);
2989 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2994 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2996 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2998 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2999 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
3000 } else hmenutmp
= 0;
3002 /* try to move to the next item */
3003 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
3004 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
3006 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
3007 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
3008 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
3009 pmt
->hTopMenu
, TRUE
, wFlags
);
3013 static void CALLBACK
release_capture( BOOL __normal
)
3015 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3018 /***********************************************************************
3021 * Menu tracking code.
3023 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
3024 HWND hwnd
, const RECT
*lprect
)
3029 INT executedMenuId
= -1;
3031 BOOL enterIdleSent
= FALSE
;
3035 mt
.hCurrentMenu
= hmenu
;
3036 mt
.hTopMenu
= hmenu
;
3037 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
3041 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3042 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3045 if (!(menu
= MENU_GetMenu( hmenu
)))
3047 WARN("Invalid menu handle %p\n", hmenu
);
3048 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3052 if (wFlags
& TPM_BUTTONDOWN
)
3054 /* Get the result in order to start the tracking or not */
3055 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3056 fEndMenu
= !fRemove
;
3059 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3061 /* owner may not be visible when tracking a popup, so use the menu itself */
3062 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3063 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3065 __TRY
while (!fEndMenu
)
3067 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3068 if (!menu
) /* sometimes happens if I do a window manager close */
3071 /* we have to keep the message in the queue until it's
3072 * clear that menu loop is not over yet. */
3076 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3078 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3079 /* remove the message from the queue */
3080 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3086 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3087 enterIdleSent
= TRUE
;
3088 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3094 /* check if EndMenu() tried to cancel us, by posting this message */
3095 if(msg
.message
== WM_CANCELMODE
)
3097 /* we are now out of the loop */
3100 /* remove the message from the queue */
3101 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3103 /* break out of internal loop, ala ESCAPE */
3107 TranslateMessage( &msg
);
3110 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3111 enterIdleSent
=FALSE
;
3114 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3117 * Use the mouse coordinates in lParam instead of those in the MSG
3118 * struct to properly handle synthetic messages. They are already
3119 * in screen coordinates.
3121 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3122 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3124 /* Find a menu for this mouse event */
3125 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3129 /* no WM_NC... messages in captured state */
3131 case WM_RBUTTONDBLCLK
:
3132 case WM_RBUTTONDOWN
:
3133 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3135 case WM_LBUTTONDBLCLK
:
3136 case WM_LBUTTONDOWN
:
3137 /* If the message belongs to the menu, removes it from the queue */
3138 /* Else, end menu tracking */
3139 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3140 fEndMenu
= !fRemove
;
3144 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3147 /* Check if a menu was selected by the mouse */
3150 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3151 TRACE("executedMenuId %d\n", executedMenuId
);
3153 /* End the loop if executedMenuId is an item ID */
3154 /* or if the job was done (executedMenuId = 0). */
3155 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3157 /* No menu was selected by the mouse */
3158 /* if the function was called by TrackPopupMenu, continue
3159 with the menu tracking. If not, stop it */
3161 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3166 /* the selected menu item must be changed every time */
3167 /* the mouse moves. */
3170 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3172 } /* switch(msg.message) - mouse */
3174 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3176 fRemove
= TRUE
; /* Keyboard messages are always removed */
3189 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3190 NO_SELECTED_ITEM
, FALSE
, 0 );
3191 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3192 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3196 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3198 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3199 if (!(menu
->wFlags
& MF_POPUP
))
3200 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3201 else /* otherwise try to move selection */
3202 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3203 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3207 MENU_KeyLeft( &mt
, wFlags
);
3211 MENU_KeyRight( &mt
, wFlags
);
3215 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3221 hi
.cbSize
= sizeof(HELPINFO
);
3222 hi
.iContextType
= HELPINFO_MENUITEM
;
3223 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3226 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3227 hi
.hItemHandle
= hmenu
;
3228 hi
.dwContextId
= menu
->dwContextHelpID
;
3229 hi
.MousePos
= msg
.pt
;
3230 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3237 break; /* WM_KEYDOWN */
3244 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3246 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3247 fEndMenu
= (executedMenuId
!= -2);
3252 /* Hack to avoid control chars. */
3253 /* We will find a better way real soon... */
3254 if (msg
.wParam
< 32) break;
3256 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3257 LOWORD(msg
.wParam
), FALSE
);
3258 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3259 else if (pos
== (UINT
)-1) MessageBeep(0);
3262 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3264 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3265 fEndMenu
= (executedMenuId
!= -2);
3269 } /* switch(msg.message) - kbd */
3273 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3274 DispatchMessageW( &msg
);
3278 if (!fEndMenu
) fRemove
= TRUE
;
3280 /* finally remove message from the queue */
3282 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3283 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3284 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3286 __FINALLY( release_capture
)
3288 /* If dropdown is still painted and the close box is clicked on
3289 then the menu will be destroyed as part of the DispatchMessage above.
3290 This will then invalidate the menu handle in mt.hTopMenu. We should
3291 check for this first. */
3292 if( IsMenu( mt
.hTopMenu
) )
3294 menu
= MENU_GetMenu( mt
.hTopMenu
);
3296 if( IsWindow( mt
.hOwnerWnd
) )
3298 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3300 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3302 DestroyWindow( menu
->hWnd
);
3305 if (!(wFlags
& TPM_NONOTIFY
))
3306 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3307 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3309 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3310 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3313 /* Reset the variable for hiding menu */
3314 if( menu
) menu
->bTimeToHide
= FALSE
;
3317 /* The return value is only used by TrackPopupMenu */
3318 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3319 if (executedMenuId
== -1) executedMenuId
= 0;
3320 return executedMenuId
;
3323 /***********************************************************************
3326 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3330 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3334 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3335 if (!(wFlags
& TPM_NONOTIFY
))
3336 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3338 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3340 if (!(wFlags
& TPM_NONOTIFY
))
3342 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3343 /* If an app changed/recreated menu bar entries in WM_INITMENU
3344 * menu sizes will be recalculated once the menu created/shown.
3348 /* This makes the menus of applications built with Delphi work.
3349 * It also enables menus to be displayed in more than one window,
3350 * but there are some bugs left that need to be fixed in this case.
3352 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3356 /***********************************************************************
3359 static BOOL
MENU_ExitTracking(HWND hWnd
)
3361 TRACE("hwnd=%p\n", hWnd
);
3363 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3369 /***********************************************************************
3370 * MENU_TrackMouseMenuBar
3372 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3374 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3376 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3377 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3379 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3383 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3384 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3385 MENU_ExitTracking(hWnd
);
3390 /***********************************************************************
3391 * MENU_TrackKbdMenuBar
3393 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3395 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3397 UINT uItem
= NO_SELECTED_ITEM
;
3399 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3401 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3403 /* find window that has a menu */
3405 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3406 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3408 /* check if we have to track a system menu */
3410 hTrackMenu
= GetMenu( hwnd
);
3411 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3413 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3414 hTrackMenu
= get_win_sys_menu( hwnd
);
3416 wParam
|= HTSYSMENU
; /* prevent item lookup */
3419 if (!IsMenu( hTrackMenu
)) return;
3421 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3423 if( wChar
&& wChar
!= ' ' )
3425 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3426 if ( uItem
>= (UINT
)(-2) )
3428 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3429 /* schedule end of menu tracking */
3430 wFlags
|= TF_ENDMENU
;
3435 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3437 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3439 if( uItem
== NO_SELECTED_ITEM
)
3440 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3442 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3446 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3447 MENU_ExitTracking( hwnd
);
3450 /**********************************************************************
3451 * TrackPopupMenuEx (USER32.@)
3453 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3454 HWND hWnd
, LPTPMPARAMS lpTpm
)
3458 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3459 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3460 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3462 /* Parameter check */
3463 /* FIXME: this check is performed several times, here and in the called
3464 functions. That could be optimized */
3465 if (!MENU_GetMenu( hMenu
))
3467 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3471 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3473 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3474 if (!(wFlags
& TPM_NONOTIFY
))
3475 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3477 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3478 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3479 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3480 MENU_ExitTracking(hWnd
);
3485 /**********************************************************************
3486 * TrackPopupMenu (USER32.@)
3488 * Like the win32 API, the function return the command ID only if the
3489 * flag TPM_RETURNCMD is on.
3492 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3493 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3495 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3498 /***********************************************************************
3501 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3503 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3505 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3511 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3512 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3516 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3517 return MA_NOACTIVATE
;
3522 BeginPaint( hwnd
, &ps
);
3523 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3524 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3525 EndPaint( hwnd
, &ps
);
3532 /* zero out global pointer in case resident popup window was destroyed. */
3533 if (hwnd
== top_popup
) top_popup
= 0;
3540 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3543 SetWindowLongPtrW( hwnd
, 0, 0 );
3546 case MM_SETMENUHANDLE
:
3547 SetWindowLongPtrW( hwnd
, 0, wParam
);
3550 case MM_GETMENUHANDLE
:
3551 return GetWindowLongPtrW( hwnd
, 0 );
3554 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3560 /***********************************************************************
3561 * MENU_GetMenuBarHeight
3563 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3565 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3566 INT orgX
, INT orgY
)
3572 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3574 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3576 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3577 SelectObject( hdc
, get_menu_font(FALSE
));
3578 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3579 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3580 ReleaseDC( hwnd
, hdc
);
3581 return lppop
->Height
;
3585 /*******************************************************************
3586 * ChangeMenuA (USER32.@)
3588 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3589 UINT id
, UINT flags
)
3591 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3592 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3594 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3595 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3597 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3598 flags
& MF_BYPOSITION
? pos
: id
,
3599 flags
& ~MF_REMOVE
);
3600 /* Default: MF_INSERT */
3601 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3605 /*******************************************************************
3606 * ChangeMenuW (USER32.@)
3608 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3609 UINT id
, UINT flags
)
3611 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3612 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3614 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3615 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3617 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3618 flags
& MF_BYPOSITION
? pos
: id
,
3619 flags
& ~MF_REMOVE
);
3620 /* Default: MF_INSERT */
3621 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3625 /*******************************************************************
3626 * CheckMenuItem (USER32.@)
3628 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3633 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3634 ret
= item
->fState
& MF_CHECKED
;
3635 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3636 else item
->fState
&= ~MF_CHECKED
;
3641 /**********************************************************************
3642 * EnableMenuItem (USER32.@)
3644 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3650 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3652 /* Get the Popupmenu to access the owner menu */
3653 if (!(menu
= MENU_GetMenu(hMenu
)))
3656 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3659 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3660 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3662 /* If the close item in the system menu change update the close button */
3663 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3665 if (menu
->hSysMenuOwner
!= 0)
3668 POPUPMENU
* parentMenu
;
3670 /* Get the parent menu to access*/
3671 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3674 /* Refresh the frame to reflect the change */
3675 GetWindowRect(parentMenu
->hWnd
, &rc
);
3676 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3678 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3686 /*******************************************************************
3687 * GetMenuStringA (USER32.@)
3689 INT WINAPI
GetMenuStringA(
3690 HMENU hMenu
, /* [in] menuhandle */
3691 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3692 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3693 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3694 UINT wFlags
/* [in] MF_ flags */
3698 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3699 if (str
&& nMaxSiz
) str
[0] = '\0';
3700 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3701 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3704 if (!item
->text
) return 0;
3705 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3706 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3708 TRACE("returning %s\n", debugstr_a(str
));
3713 /*******************************************************************
3714 * GetMenuStringW (USER32.@)
3716 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3717 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3721 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3722 if (str
&& nMaxSiz
) str
[0] = '\0';
3723 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3724 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3727 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3728 if( !(item
->text
)) {
3732 lstrcpynW( str
, item
->text
, nMaxSiz
);
3733 TRACE("returning %s\n", debugstr_w(str
));
3734 return strlenW(str
);
3738 /**********************************************************************
3739 * HiliteMenuItem (USER32.@)
3741 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3745 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3746 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3747 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3748 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3749 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3750 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3755 /**********************************************************************
3756 * GetMenuState (USER32.@)
3758 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3761 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3762 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3763 debug_print_menuitem (" item: ", item
, "");
3764 if (item
->fType
& MF_POPUP
)
3766 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3767 if (!menu
) return -1;
3768 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3772 /* We used to (from way back then) mask the result to 0xff. */
3773 /* I don't know why and it seems wrong as the documented */
3774 /* return flag MF_SEPARATOR is outside that mask. */
3775 return (item
->fType
| item
->fState
);
3780 /**********************************************************************
3781 * GetMenuItemCount (USER32.@)
3783 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3785 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3786 if (!menu
) return -1;
3787 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3788 return menu
->nItems
;
3792 /**********************************************************************
3793 * GetMenuItemID (USER32.@)
3795 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3799 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3800 if (lpmi
->fType
& MF_POPUP
) return -1;
3806 /*******************************************************************
3807 * InsertMenuW (USER32.@)
3809 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3810 UINT_PTR id
, LPCWSTR str
)
3814 if (IS_STRING_ITEM(flags
) && str
)
3815 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3816 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3817 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3818 hMenu
, pos
, flags
, id
, str
);
3820 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3822 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3824 RemoveMenu( hMenu
, pos
, flags
);
3828 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3833 /*******************************************************************
3834 * InsertMenuA (USER32.@)
3836 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3837 UINT_PTR id
, LPCSTR str
)
3841 if (IS_STRING_ITEM(flags
) && str
)
3843 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3844 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3847 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3848 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3849 HeapFree( GetProcessHeap(), 0, newstr
);
3853 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3857 /*******************************************************************
3858 * AppendMenuA (USER32.@)
3860 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3861 UINT_PTR id
, LPCSTR data
)
3863 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3867 /*******************************************************************
3868 * AppendMenuW (USER32.@)
3870 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3871 UINT_PTR id
, LPCWSTR data
)
3873 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3877 /**********************************************************************
3878 * RemoveMenu (USER32.@)
3880 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3885 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3886 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3887 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3891 MENU_FreeItemData( item
);
3893 if (--menu
->nItems
== 0)
3895 HeapFree( GetProcessHeap(), 0, menu
->items
);
3900 while(nPos
< menu
->nItems
)
3906 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3907 menu
->nItems
* sizeof(MENUITEM
) );
3913 /**********************************************************************
3914 * DeleteMenu (USER32.@)
3916 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3918 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3919 if (!item
) return FALSE
;
3920 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3921 /* nPos is now the position of the item */
3922 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3927 /*******************************************************************
3928 * ModifyMenuW (USER32.@)
3930 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3931 UINT_PTR id
, LPCWSTR str
)
3935 if (IS_STRING_ITEM(flags
))
3936 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3938 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3940 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3941 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3942 return MENU_SetItemData( item
, flags
, id
, str
);
3946 /*******************************************************************
3947 * ModifyMenuA (USER32.@)
3949 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3950 UINT_PTR id
, LPCSTR str
)
3954 if (IS_STRING_ITEM(flags
) && str
)
3956 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3957 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3960 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3961 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3962 HeapFree( GetProcessHeap(), 0, newstr
);
3966 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3970 /**********************************************************************
3971 * CreatePopupMenu (USER32.@)
3973 HMENU WINAPI
CreatePopupMenu(void)
3978 if (!(hmenu
= CreateMenu())) return 0;
3979 menu
= MENU_GetMenu( hmenu
);
3980 menu
->wFlags
|= MF_POPUP
;
3981 menu
->bTimeToHide
= FALSE
;
3986 /**********************************************************************
3987 * GetMenuCheckMarkDimensions (USER.417)
3988 * GetMenuCheckMarkDimensions (USER32.@)
3990 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3992 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3996 /**********************************************************************
3997 * SetMenuItemBitmaps (USER32.@)
3999 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4000 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4004 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4006 if (!hNewCheck
&& !hNewUnCheck
)
4008 item
->fState
&= ~MF_USECHECKBITMAPS
;
4010 else /* Install new bitmaps */
4012 item
->hCheckBit
= hNewCheck
;
4013 item
->hUnCheckBit
= hNewUnCheck
;
4014 item
->fState
|= MF_USECHECKBITMAPS
;
4020 /**********************************************************************
4021 * CreateMenu (USER32.@)
4023 HMENU WINAPI
CreateMenu(void)
4027 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
4028 menu
= USER_HEAP_LIN_ADDR(hMenu
);
4030 ZeroMemory(menu
, sizeof(POPUPMENU
));
4031 menu
->wMagic
= MENU_MAGIC
;
4032 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4033 menu
->bTimeToHide
= FALSE
;
4035 TRACE("return %p\n", hMenu
);
4041 /**********************************************************************
4042 * DestroyMenu (USER32.@)
4044 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4046 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
4048 TRACE("(%p)\n", hMenu
);
4051 if (!lppop
) return FALSE
;
4053 lppop
->wMagic
= 0; /* Mark it as destroyed */
4055 /* DestroyMenu should not destroy system menu popup owner */
4056 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4058 DestroyWindow( lppop
->hWnd
);
4062 if (lppop
->items
) /* recursively destroy submenus */
4065 MENUITEM
*item
= lppop
->items
;
4066 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4068 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4069 MENU_FreeItemData( item
);
4071 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4073 USER_HEAP_FREE( hMenu
);
4078 /**********************************************************************
4079 * GetSystemMenu (USER32.@)
4081 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4083 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4086 if (wndPtr
== WND_DESKTOP
) return 0;
4087 if (wndPtr
== WND_OTHER_PROCESS
)
4089 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4093 if (wndPtr
->hSysMenu
&& bRevert
)
4095 DestroyMenu(wndPtr
->hSysMenu
);
4096 wndPtr
->hSysMenu
= 0;
4099 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4100 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4102 if( wndPtr
->hSysMenu
)
4105 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4107 /* Store the dummy sysmenu handle to facilitate the refresh */
4108 /* of the close button if the SC_CLOSE item change */
4109 menu
= MENU_GetMenu(retvalue
);
4111 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4113 WIN_ReleasePtr( wndPtr
);
4115 return bRevert
? 0 : retvalue
;
4119 /*******************************************************************
4120 * SetSystemMenu (USER32.@)
4122 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4124 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4126 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4128 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4129 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4130 WIN_ReleasePtr( wndPtr
);
4137 /**********************************************************************
4138 * GetMenu (USER32.@)
4140 HMENU WINAPI
GetMenu( HWND hWnd
)
4142 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4143 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4147 /**********************************************************************
4148 * GetMenuBarInfo (USER32.@)
4150 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4152 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4156 /**********************************************************************
4159 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4160 * SetWindowPos call that would result if SetMenu were called directly.
4162 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4164 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4166 if (hMenu
&& !IsMenu(hMenu
))
4168 WARN("hMenu %p is not a menu handle\n", hMenu
);
4171 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4174 hWnd
= WIN_GetFullHandle( hWnd
);
4175 if (GetCapture() == hWnd
)
4176 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4182 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4184 lpmenu
->hWnd
= hWnd
;
4185 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4187 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4192 /**********************************************************************
4193 * SetMenu (USER32.@)
4195 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4197 if(!MENU_SetMenu(hWnd
, hMenu
))
4200 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4201 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4206 /**********************************************************************
4207 * GetSubMenu (USER32.@)
4209 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4213 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4214 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4215 return lpmi
->hSubMenu
;
4219 /**********************************************************************
4220 * DrawMenuBar (USER32.@)
4222 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4225 HMENU hMenu
= GetMenu(hWnd
);
4227 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4229 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4231 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4232 lppop
->hwndOwner
= hWnd
;
4233 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4234 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4238 /***********************************************************************
4239 * DrawMenuBarTemp (USER32.@)
4243 * called by W98SE desk.cpl Control Panel Applet
4245 * Not 100% sure about the param names, but close.
4247 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4252 BOOL flat_menu
= FALSE
;
4254 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4257 hMenu
= GetMenu(hwnd
);
4260 hFont
= get_menu_font(FALSE
);
4262 lppop
= MENU_GetMenu( hMenu
);
4263 if (lppop
== NULL
|| lprect
== NULL
)
4265 retvalue
= GetSystemMetrics(SM_CYMENU
);
4269 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4271 hfontOld
= SelectObject( hDC
, hFont
);
4273 if (lppop
->Height
== 0)
4274 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4276 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4278 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4280 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4281 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4282 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4284 if (lppop
->nItems
== 0)
4286 retvalue
= GetSystemMetrics(SM_CYMENU
);
4290 for (i
= 0; i
< lppop
->nItems
; i
++)
4292 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4293 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4295 retvalue
= lppop
->Height
;
4298 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4302 /***********************************************************************
4303 * EndMenu (USER.187)
4304 * EndMenu (USER32.@)
4306 BOOL WINAPI
EndMenu(void)
4308 /* if we are in the menu code, and it is active */
4309 if (!fEndMenu
&& top_popup
)
4311 /* terminate the menu handling code */
4314 /* needs to be posted to wakeup the internal menu handler */
4315 /* which will now terminate the menu, in the event that */
4316 /* the main window was minimized, or lost focus, so we */
4317 /* don't end up with an orphaned menu */
4318 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4324 /***********************************************************************
4325 * LookupMenuHandle (USER.217)
4327 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4329 HMENU hmenu32
= HMENU_32(hmenu
);
4331 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4332 else return HMENU_16(hmenu32
);
4336 /**********************************************************************
4337 * LoadMenu (USER.150)
4339 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4345 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4346 if (!name
) return 0;
4348 instance
= GetExePtr( instance
);
4349 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4350 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4351 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4352 FreeResource16( handle
);
4357 /*****************************************************************
4358 * LoadMenuA (USER32.@)
4360 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4362 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4363 if (!hrsrc
) return 0;
4364 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4368 /*****************************************************************
4369 * LoadMenuW (USER32.@)
4371 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4373 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4374 if (!hrsrc
) return 0;
4375 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4379 /**********************************************************************
4380 * LoadMenuIndirect (USER.220)
4382 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4385 WORD version
, offset
;
4386 LPCSTR p
= template;
4388 TRACE("(%p)\n", template );
4389 version
= GET_WORD(p
);
4393 WARN("version must be 0 for Win16\n" );
4396 offset
= GET_WORD(p
);
4397 p
+= sizeof(WORD
) + offset
;
4398 if (!(hMenu
= CreateMenu())) return 0;
4399 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4401 DestroyMenu( hMenu
);
4404 return HMENU_16(hMenu
);
4408 /**********************************************************************
4409 * LoadMenuIndirectW (USER32.@)
4411 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4414 WORD version
, offset
;
4415 LPCSTR p
= template;
4417 version
= GET_WORD(p
);
4419 TRACE("%p, ver %d\n", template, version
);
4422 case 0: /* standard format is version of 0 */
4423 offset
= GET_WORD(p
);
4424 p
+= sizeof(WORD
) + offset
;
4425 if (!(hMenu
= CreateMenu())) return 0;
4426 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4428 DestroyMenu( hMenu
);
4432 case 1: /* extended format is version of 1 */
4433 offset
= GET_WORD(p
);
4434 p
+= sizeof(WORD
) + offset
;
4435 if (!(hMenu
= CreateMenu())) return 0;
4436 if (!MENUEX_ParseResource( p
, hMenu
))
4438 DestroyMenu( hMenu
);
4443 ERR("version %d not supported.\n", version
);
4449 /**********************************************************************
4450 * LoadMenuIndirectA (USER32.@)
4452 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4454 return LoadMenuIndirectW( template );
4458 /**********************************************************************
4461 BOOL WINAPI
IsMenu(HMENU hmenu
)
4463 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4467 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4473 /**********************************************************************
4474 * GetMenuItemInfo_common
4477 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4478 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4480 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4482 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4485 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4489 if( lpmii
->fMask
& MIIM_TYPE
) {
4490 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4491 WARN("invalid combination of fMask bits used\n");
4492 /* this does not happen on Win9x/ME */
4493 SetLastError( ERROR_INVALID_PARAMETER
);
4496 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4497 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4498 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4499 if( lpmii
->fType
& MFT_BITMAP
) {
4500 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4502 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4503 /* this does not happen on Win9x/ME */
4504 lpmii
->dwTypeData
= 0;
4509 /* copy the text string */
4510 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4512 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4515 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4517 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4523 len
= strlenW(menu
->text
);
4524 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4525 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4529 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4530 0, NULL
, NULL
) - 1;
4531 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4532 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4533 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4534 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4536 /* if we've copied a substring we return its length */
4537 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4538 if (lpmii
->cch
<= len
+ 1)
4543 /* return length of string */
4544 /* not on Win9x/ME if fType & MFT_BITMAP */
4550 if (lpmii
->fMask
& MIIM_FTYPE
)
4551 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4553 if (lpmii
->fMask
& MIIM_BITMAP
)
4554 lpmii
->hbmpItem
= menu
->hbmpItem
;
4556 if (lpmii
->fMask
& MIIM_STATE
)
4557 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4559 if (lpmii
->fMask
& MIIM_ID
)
4560 lpmii
->wID
= menu
->wID
;
4562 if (lpmii
->fMask
& MIIM_SUBMENU
)
4563 lpmii
->hSubMenu
= menu
->hSubMenu
;
4565 /* hSubMenu is always cleared
4566 * (not on Win9x/ME ) */
4567 lpmii
->hSubMenu
= 0;
4570 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4571 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4572 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4574 if (lpmii
->fMask
& MIIM_DATA
)
4575 lpmii
->dwItemData
= menu
->dwItemData
;
4580 /**********************************************************************
4581 * GetMenuItemInfoA (USER32.@)
4583 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4584 LPMENUITEMINFOA lpmii
)
4588 if( lpmii
->cbSize
!= sizeof( mii
) &&
4589 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4590 SetLastError( ERROR_INVALID_PARAMETER
);
4593 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4594 mii
.cbSize
= sizeof( mii
);
4595 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4596 (LPMENUITEMINFOW
)&mii
, FALSE
);
4597 mii
.cbSize
= lpmii
->cbSize
;
4598 memcpy( lpmii
, &mii
, mii
.cbSize
);
4602 /**********************************************************************
4603 * GetMenuItemInfoW (USER32.@)
4605 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4606 LPMENUITEMINFOW lpmii
)
4610 if( lpmii
->cbSize
!= sizeof( mii
) &&
4611 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4612 SetLastError( ERROR_INVALID_PARAMETER
);
4615 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4616 mii
.cbSize
= sizeof( mii
);
4617 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4618 mii
.cbSize
= lpmii
->cbSize
;
4619 memcpy( lpmii
, &mii
, mii
.cbSize
);
4624 /* set a menu item text from a ASCII or Unicode string */
4625 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4631 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4632 strcpyW( menu
->text
, text
);
4636 LPCSTR str
= (LPCSTR
)text
;
4637 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4638 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4639 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4644 /**********************************************************************
4645 * SetMenuItemInfo_common
4648 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4649 const MENUITEMINFOW
*lpmii
,
4652 if (!menu
) return FALSE
;
4654 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4656 if (lpmii
->fMask
& MIIM_TYPE
) {
4657 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4658 WARN("invalid combination of fMask bits used\n");
4659 /* this does not happen on Win9x/ME */
4660 SetLastError( ERROR_INVALID_PARAMETER
);
4664 /* Remove the old type bits and replace them with the new ones */
4665 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4666 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4668 if (IS_STRING_ITEM(menu
->fType
)) {
4669 HeapFree(GetProcessHeap(), 0, menu
->text
);
4670 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4671 } else if( (menu
->fType
) & MFT_BITMAP
)
4672 menu
->hbmpItem
= HBITMAP_32(LOWORD(lpmii
->dwTypeData
));
4675 if (lpmii
->fMask
& MIIM_FTYPE
) {
4676 if(( lpmii
->fType
& MFT_BITMAP
)) {
4677 SetLastError( ERROR_INVALID_PARAMETER
);
4680 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4681 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4683 if (lpmii
->fMask
& MIIM_STRING
) {
4684 /* free the string when used */
4685 HeapFree(GetProcessHeap(), 0, menu
->text
);
4686 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4689 if (lpmii
->fMask
& MIIM_STATE
)
4691 /* Other menu items having MFS_DEFAULT are not converted
4693 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4696 if (lpmii
->fMask
& MIIM_ID
)
4697 menu
->wID
= lpmii
->wID
;
4699 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4700 menu
->hSubMenu
= lpmii
->hSubMenu
;
4701 if (menu
->hSubMenu
) {
4702 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4704 subMenu
->wFlags
|= MF_POPUP
;
4705 menu
->fType
|= MF_POPUP
;
4708 SetLastError( ERROR_INVALID_PARAMETER
);
4713 menu
->fType
&= ~MF_POPUP
;
4716 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4718 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4719 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4721 if (lpmii
->fMask
& MIIM_DATA
)
4722 menu
->dwItemData
= lpmii
->dwItemData
;
4724 if (lpmii
->fMask
& MIIM_BITMAP
)
4725 menu
->hbmpItem
= lpmii
->hbmpItem
;
4727 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4728 menu
->fType
|= MFT_SEPARATOR
;
4730 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4734 /**********************************************************************
4735 * SetMenuItemInfoA (USER32.@)
4737 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4738 const MENUITEMINFOA
*lpmii
)
4742 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4744 if( lpmii
->cbSize
!= sizeof( mii
) &&
4745 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4746 SetLastError( ERROR_INVALID_PARAMETER
);
4749 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4750 if( lpmii
->cbSize
!= sizeof( mii
)) {
4751 mii
.cbSize
= sizeof( mii
);
4752 mii
.hbmpItem
= NULL
;
4754 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4755 (const MENUITEMINFOW
*)&mii
, FALSE
);
4758 /**********************************************************************
4759 * SetMenuItemInfoW (USER32.@)
4761 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4762 const MENUITEMINFOW
*lpmii
)
4766 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4768 if( lpmii
->cbSize
!= sizeof( mii
) &&
4769 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4770 SetLastError( ERROR_INVALID_PARAMETER
);
4773 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4774 if( lpmii
->cbSize
!= sizeof( mii
)) {
4775 mii
.cbSize
= sizeof( mii
);
4776 mii
.hbmpItem
= NULL
;
4778 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4779 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4782 /**********************************************************************
4783 * SetMenuDefaultItem (USER32.@)
4786 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4792 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4794 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4796 /* reset all default-item flags */
4798 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4800 item
->fState
&= ~MFS_DEFAULT
;
4803 /* no default item */
4812 if ( uItem
>= menu
->nItems
) return FALSE
;
4813 item
[uItem
].fState
|= MFS_DEFAULT
;
4818 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4820 if (item
->wID
== uItem
)
4822 item
->fState
|= MFS_DEFAULT
;
4831 /**********************************************************************
4832 * GetMenuDefaultItem (USER32.@)
4834 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4840 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4842 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4844 /* find default item */
4848 if (! item
) return -1;
4850 while ( !( item
->fState
& MFS_DEFAULT
) )
4853 if (i
>= menu
->nItems
) return -1;
4856 /* default: don't return disabled items */
4857 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4859 /* search rekursiv when needed */
4860 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4863 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4864 if ( -1 != ret
) return ret
;
4866 /* when item not found in submenu, return the popup item */
4868 return ( bypos
) ? i
: item
->wID
;
4873 /**********************************************************************
4874 * InsertMenuItemA (USER32.@)
4876 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4877 const MENUITEMINFOA
*lpmii
)
4882 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4884 if( lpmii
->cbSize
!= sizeof( mii
) &&
4885 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4886 SetLastError( ERROR_INVALID_PARAMETER
);
4889 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4890 if( lpmii
->cbSize
!= sizeof( mii
)) {
4891 mii
.cbSize
= sizeof( mii
);
4892 mii
.hbmpItem
= NULL
;
4895 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4896 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4900 /**********************************************************************
4901 * InsertMenuItemW (USER32.@)
4903 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4904 const MENUITEMINFOW
*lpmii
)
4909 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4911 if( lpmii
->cbSize
!= sizeof( mii
) &&
4912 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4913 SetLastError( ERROR_INVALID_PARAMETER
);
4916 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4917 if( lpmii
->cbSize
!= sizeof( mii
)) {
4918 mii
.cbSize
= sizeof( mii
);
4919 mii
.hbmpItem
= NULL
;
4922 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4923 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4926 /**********************************************************************
4927 * CheckMenuRadioItem (USER32.@)
4930 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4931 UINT first
, UINT last
, UINT check
,
4936 MENUITEM
*mi_first
= NULL
, *mi_check
;
4937 HMENU m_first
, m_check
;
4939 for (i
= first
; i
<= last
; i
++)
4946 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4947 if (!mi_first
) continue;
4948 mi_check
= mi_first
;
4954 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4955 if (!mi_check
) continue;
4958 if (m_first
!= m_check
) continue;
4959 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4963 mi_check
->fType
|= MFT_RADIOCHECK
;
4964 mi_check
->fState
|= MFS_CHECKED
;
4969 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4970 mi_check
->fState
&= ~MFS_CHECKED
;
4978 /**********************************************************************
4979 * GetMenuItemRect (USER32.@)
4981 * ATTENTION: Here, the returned values in rect are the screen
4982 * coordinates of the item just like if the menu was
4983 * always on the upper left side of the application.
4986 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4989 POPUPMENU
*itemMenu
;
4993 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4995 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4996 referenceHwnd
= hwnd
;
5000 itemMenu
= MENU_GetMenu(hMenu
);
5001 if (itemMenu
== NULL
)
5004 if(itemMenu
->hWnd
== 0)
5006 referenceHwnd
= itemMenu
->hWnd
;
5009 if ((rect
== NULL
) || (item
== NULL
))
5014 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
5019 /**********************************************************************
5020 * SetMenuInfo (USER32.@)
5023 * actually use the items to draw the menu
5024 * (recalculate and/or redraw)
5026 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5029 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5031 if (lpmi
->fMask
& MIM_BACKGROUND
)
5032 menu
->hbrBack
= lpmi
->hbrBack
;
5034 if (lpmi
->fMask
& MIM_HELPID
)
5035 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5037 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5038 menu
->cyMax
= lpmi
->cyMax
;
5040 if (lpmi
->fMask
& MIM_MENUDATA
)
5041 menu
->dwMenuData
= lpmi
->dwMenuData
;
5043 if (lpmi
->fMask
& MIM_STYLE
)
5044 menu
->dwStyle
= lpmi
->dwStyle
;
5046 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5048 MENUITEM
*item
= menu
->items
;
5049 for( i
= menu
->nItems
; i
; i
--, item
++)
5050 if( item
->fType
& MF_POPUP
)
5051 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5056 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5058 TRACE("(%p %p)\n", hMenu
, lpmi
);
5059 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5060 if( lpmi
->fMask
& MIM_STYLE
) {
5061 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5062 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5063 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5067 SetLastError( ERROR_INVALID_PARAMETER
);
5071 /**********************************************************************
5072 * GetMenuInfo (USER32.@)
5078 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5081 TRACE("(%p %p)\n", hMenu
, lpmi
);
5083 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5086 if (lpmi
->fMask
& MIM_BACKGROUND
)
5087 lpmi
->hbrBack
= menu
->hbrBack
;
5089 if (lpmi
->fMask
& MIM_HELPID
)
5090 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5092 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5093 lpmi
->cyMax
= menu
->cyMax
;
5095 if (lpmi
->fMask
& MIM_MENUDATA
)
5096 lpmi
->dwMenuData
= menu
->dwMenuData
;
5098 if (lpmi
->fMask
& MIM_STYLE
)
5099 lpmi
->dwStyle
= menu
->dwStyle
;
5103 SetLastError( ERROR_INVALID_PARAMETER
);
5108 /**********************************************************************
5109 * SetMenuContextHelpId (USER32.@)
5111 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5115 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5117 if ((menu
= MENU_GetMenu(hMenu
)))
5119 menu
->dwContextHelpID
= dwContextHelpID
;
5126 /**********************************************************************
5127 * GetMenuContextHelpId (USER32.@)
5129 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5133 TRACE("(%p)\n", hMenu
);
5135 if ((menu
= MENU_GetMenu(hMenu
)))
5137 return menu
->dwContextHelpID
;
5142 /**********************************************************************
5143 * MenuItemFromPoint (USER32.@)
5145 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5147 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5150 /*FIXME: Do we have to handle hWnd here? */
5151 if (!menu
) return -1;
5152 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5157 /**********************************************************************
5158 * translate_accelerator
5160 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5161 BYTE fVirt
, WORD key
, WORD cmd
)
5166 if (wParam
!= key
) return FALSE
;
5168 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5169 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5170 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5172 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5174 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5176 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5182 if(fVirt
& FVIRTKEY
)
5184 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5185 wParam
, 0xff & HIWORD(lParam
));
5187 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5188 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5192 if (!(lParam
& 0x01000000)) /* no special_key */
5194 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5195 { /* ^^ ALT pressed */
5196 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5205 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5209 HMENU hMenu
, hSubMenu
, hSysMenu
;
5210 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5212 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5213 hSysMenu
= get_win_sys_menu( hWnd
);
5215 /* find menu item and ask application to initialize it */
5216 /* 1. in the system menu */
5217 hSubMenu
= hSysMenu
;
5219 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5223 if (!IsWindowEnabled(hWnd
))
5227 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5228 if(hSubMenu
!= hSysMenu
)
5230 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5231 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5232 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5234 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5237 else /* 2. in the window's menu */
5241 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5245 if (!IsWindowEnabled(hWnd
))
5249 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5250 if(hSubMenu
!= hMenu
)
5252 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5253 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5254 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5256 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5263 if (uSysStat
!= (UINT
)-1)
5265 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5272 if (uStat
!= (UINT
)-1)
5278 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5290 if( mesg
==WM_COMMAND
)
5292 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5293 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5295 else if( mesg
==WM_SYSCOMMAND
)
5297 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5298 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5302 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5303 * #0: unknown (please report!)
5304 * #1: for WM_KEYUP,WM_SYSKEYUP
5305 * #2: mouse is captured
5306 * #3: window is disabled
5307 * #4: it's a disabled system menu option
5308 * #5: it's a menu option, but window is iconic
5309 * #6: it's a menu option, but disabled
5311 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5313 ERR_(accel
)(" unknown reason - please report!\n");
5318 /**********************************************************************
5319 * TranslateAcceleratorA (USER32.@)
5320 * TranslateAccelerator (USER32.@)
5322 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5325 LPACCEL16 lpAccelTbl
;
5329 if (!hWnd
|| !msg
) return 0;
5331 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5333 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5337 wParam
= msg
->wParam
;
5339 switch (msg
->message
)
5348 char ch
= LOWORD(wParam
);
5350 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5351 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5359 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5360 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5364 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5365 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5367 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5372 /**********************************************************************
5373 * TranslateAcceleratorW (USER32.@)
5375 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5378 LPACCEL16 lpAccelTbl
;
5381 if (!hWnd
|| !msg
) return 0;
5383 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5385 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5389 switch (msg
->message
)
5401 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5402 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5406 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5407 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5409 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);