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 MAKELONG(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
, MAKELONG(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 MAKELONG( 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, 0, 0, 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
, MAKELONG(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
);
3451 /**********************************************************************
3452 * TrackPopupMenu (USER32.@)
3454 * Like the win32 API, the function return the command ID only if the
3455 * flag TPM_RETURNCMD is on.
3458 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3459 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3463 TRACE("hmenu %p flags %04x (%d,%d) reserved %d hwnd %p rect %s\n",
3464 hMenu
, wFlags
, x
, y
, nReserved
, hWnd
, wine_dbgstr_rect(lpRect
));
3466 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3468 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3469 if (!(wFlags
& TPM_NONOTIFY
))
3470 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3472 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3473 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3474 MENU_ExitTracking(hWnd
);
3479 /**********************************************************************
3480 * TrackPopupMenuEx (USER32.@)
3482 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3483 HWND hWnd
, LPTPMPARAMS lpTpm
)
3485 FIXME("not fully implemented\n" );
3486 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3487 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3490 /***********************************************************************
3493 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3495 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3497 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3503 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3504 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3508 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3509 return MA_NOACTIVATE
;
3514 BeginPaint( hwnd
, &ps
);
3515 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3516 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3517 EndPaint( hwnd
, &ps
);
3524 /* zero out global pointer in case resident popup window was destroyed. */
3525 if (hwnd
== top_popup
) top_popup
= 0;
3532 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3535 SetWindowLongPtrW( hwnd
, 0, 0 );
3538 case MM_SETMENUHANDLE
:
3539 SetWindowLongPtrW( hwnd
, 0, wParam
);
3542 case MM_GETMENUHANDLE
:
3543 return GetWindowLongPtrW( hwnd
, 0 );
3546 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3552 /***********************************************************************
3553 * MENU_GetMenuBarHeight
3555 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3557 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3558 INT orgX
, INT orgY
)
3564 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3566 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3568 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3569 SelectObject( hdc
, get_menu_font(FALSE
));
3570 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3571 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3572 ReleaseDC( hwnd
, hdc
);
3573 return lppop
->Height
;
3577 /*******************************************************************
3578 * ChangeMenuA (USER32.@)
3580 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3581 UINT id
, UINT flags
)
3583 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3584 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3586 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3587 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3589 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3590 flags
& MF_BYPOSITION
? pos
: id
,
3591 flags
& ~MF_REMOVE
);
3592 /* Default: MF_INSERT */
3593 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3597 /*******************************************************************
3598 * ChangeMenuW (USER32.@)
3600 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3601 UINT id
, UINT flags
)
3603 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3604 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3606 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3607 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3609 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3610 flags
& MF_BYPOSITION
? pos
: id
,
3611 flags
& ~MF_REMOVE
);
3612 /* Default: MF_INSERT */
3613 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3617 /*******************************************************************
3618 * CheckMenuItem (USER32.@)
3620 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3625 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3626 ret
= item
->fState
& MF_CHECKED
;
3627 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3628 else item
->fState
&= ~MF_CHECKED
;
3633 /**********************************************************************
3634 * EnableMenuItem (USER32.@)
3636 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3642 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3644 /* Get the Popupmenu to access the owner menu */
3645 if (!(menu
= MENU_GetMenu(hMenu
)))
3648 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3651 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3652 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3654 /* If the close item in the system menu change update the close button */
3655 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3657 if (menu
->hSysMenuOwner
!= 0)
3660 POPUPMENU
* parentMenu
;
3662 /* Get the parent menu to access*/
3663 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3666 /* Refresh the frame to reflect the change */
3667 GetWindowRect(parentMenu
->hWnd
, &rc
);
3668 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3670 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3678 /*******************************************************************
3679 * GetMenuStringA (USER32.@)
3681 INT WINAPI
GetMenuStringA(
3682 HMENU hMenu
, /* [in] menuhandle */
3683 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3684 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3685 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3686 UINT wFlags
/* [in] MF_ flags */
3690 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3691 if (str
&& nMaxSiz
) str
[0] = '\0';
3692 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3693 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3696 if (!item
->text
) return 0;
3697 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3698 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3700 TRACE("returning %s\n", debugstr_a(str
));
3705 /*******************************************************************
3706 * GetMenuStringW (USER32.@)
3708 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3709 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3713 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3714 if (str
&& nMaxSiz
) str
[0] = '\0';
3715 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3716 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3719 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3720 if( !(item
->text
)) {
3724 lstrcpynW( str
, item
->text
, nMaxSiz
);
3725 TRACE("returning %s\n", debugstr_w(str
));
3726 return strlenW(str
);
3730 /**********************************************************************
3731 * HiliteMenuItem (USER32.@)
3733 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3737 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3738 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3739 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3740 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3741 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3742 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3747 /**********************************************************************
3748 * GetMenuState (USER32.@)
3750 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3753 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3754 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3755 debug_print_menuitem (" item: ", item
, "");
3756 if (item
->fType
& MF_POPUP
)
3758 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3759 if (!menu
) return -1;
3760 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3764 /* We used to (from way back then) mask the result to 0xff. */
3765 /* I don't know why and it seems wrong as the documented */
3766 /* return flag MF_SEPARATOR is outside that mask. */
3767 return (item
->fType
| item
->fState
);
3772 /**********************************************************************
3773 * GetMenuItemCount (USER32.@)
3775 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3777 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3778 if (!menu
) return -1;
3779 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3780 return menu
->nItems
;
3784 /**********************************************************************
3785 * GetMenuItemID (USER32.@)
3787 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3791 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3792 if (lpmi
->fType
& MF_POPUP
) return -1;
3798 /*******************************************************************
3799 * InsertMenuW (USER32.@)
3801 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3802 UINT_PTR id
, LPCWSTR str
)
3806 if (IS_STRING_ITEM(flags
) && str
)
3807 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3808 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3809 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3810 hMenu
, pos
, flags
, id
, str
);
3812 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3814 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3816 RemoveMenu( hMenu
, pos
, flags
);
3820 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3825 /*******************************************************************
3826 * InsertMenuA (USER32.@)
3828 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3829 UINT_PTR id
, LPCSTR str
)
3833 if (IS_STRING_ITEM(flags
) && str
)
3835 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3836 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3839 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3840 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3841 HeapFree( GetProcessHeap(), 0, newstr
);
3845 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3849 /*******************************************************************
3850 * AppendMenuA (USER32.@)
3852 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3853 UINT_PTR id
, LPCSTR data
)
3855 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3859 /*******************************************************************
3860 * AppendMenuW (USER32.@)
3862 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3863 UINT_PTR id
, LPCWSTR data
)
3865 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3869 /**********************************************************************
3870 * RemoveMenu (USER32.@)
3872 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3877 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3878 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3879 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3883 MENU_FreeItemData( item
);
3885 if (--menu
->nItems
== 0)
3887 HeapFree( GetProcessHeap(), 0, menu
->items
);
3892 while(nPos
< menu
->nItems
)
3898 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3899 menu
->nItems
* sizeof(MENUITEM
) );
3905 /**********************************************************************
3906 * DeleteMenu (USER32.@)
3908 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3910 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3911 if (!item
) return FALSE
;
3912 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3913 /* nPos is now the position of the item */
3914 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3919 /*******************************************************************
3920 * ModifyMenuW (USER32.@)
3922 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3923 UINT_PTR id
, LPCWSTR str
)
3927 if (IS_STRING_ITEM(flags
))
3928 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3930 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3932 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3933 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3934 return MENU_SetItemData( item
, flags
, id
, str
);
3938 /*******************************************************************
3939 * ModifyMenuA (USER32.@)
3941 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3942 UINT_PTR id
, LPCSTR str
)
3946 if (IS_STRING_ITEM(flags
) && str
)
3948 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3949 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3952 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3953 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3954 HeapFree( GetProcessHeap(), 0, newstr
);
3958 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3962 /**********************************************************************
3963 * CreatePopupMenu (USER32.@)
3965 HMENU WINAPI
CreatePopupMenu(void)
3970 if (!(hmenu
= CreateMenu())) return 0;
3971 menu
= MENU_GetMenu( hmenu
);
3972 menu
->wFlags
|= MF_POPUP
;
3973 menu
->bTimeToHide
= FALSE
;
3978 /**********************************************************************
3979 * GetMenuCheckMarkDimensions (USER.417)
3980 * GetMenuCheckMarkDimensions (USER32.@)
3982 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3984 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3988 /**********************************************************************
3989 * SetMenuItemBitmaps (USER32.@)
3991 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3992 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3996 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3998 if (!hNewCheck
&& !hNewUnCheck
)
4000 item
->fState
&= ~MF_USECHECKBITMAPS
;
4002 else /* Install new bitmaps */
4004 item
->hCheckBit
= hNewCheck
;
4005 item
->hUnCheckBit
= hNewUnCheck
;
4006 item
->fState
|= MF_USECHECKBITMAPS
;
4012 /**********************************************************************
4013 * CreateMenu (USER32.@)
4015 HMENU WINAPI
CreateMenu(void)
4019 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
4020 menu
= USER_HEAP_LIN_ADDR(hMenu
);
4022 ZeroMemory(menu
, sizeof(POPUPMENU
));
4023 menu
->wMagic
= MENU_MAGIC
;
4024 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4025 menu
->bTimeToHide
= FALSE
;
4027 TRACE("return %p\n", hMenu
);
4033 /**********************************************************************
4034 * DestroyMenu (USER32.@)
4036 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4038 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
4040 TRACE("(%p)\n", hMenu
);
4043 if (!lppop
) return FALSE
;
4045 lppop
->wMagic
= 0; /* Mark it as destroyed */
4047 /* DestroyMenu should not destroy system menu popup owner */
4048 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4050 DestroyWindow( lppop
->hWnd
);
4054 if (lppop
->items
) /* recursively destroy submenus */
4057 MENUITEM
*item
= lppop
->items
;
4058 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4060 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4061 MENU_FreeItemData( item
);
4063 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4065 USER_HEAP_FREE( hMenu
);
4070 /**********************************************************************
4071 * GetSystemMenu (USER32.@)
4073 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4075 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4078 if (wndPtr
== WND_DESKTOP
) return 0;
4079 if (wndPtr
== WND_OTHER_PROCESS
)
4081 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4085 if (wndPtr
->hSysMenu
&& bRevert
)
4087 DestroyMenu(wndPtr
->hSysMenu
);
4088 wndPtr
->hSysMenu
= 0;
4091 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4092 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4094 if( wndPtr
->hSysMenu
)
4097 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4099 /* Store the dummy sysmenu handle to facilitate the refresh */
4100 /* of the close button if the SC_CLOSE item change */
4101 menu
= MENU_GetMenu(retvalue
);
4103 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4105 WIN_ReleasePtr( wndPtr
);
4107 return bRevert
? 0 : retvalue
;
4111 /*******************************************************************
4112 * SetSystemMenu (USER32.@)
4114 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4116 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4118 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4120 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4121 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4122 WIN_ReleasePtr( wndPtr
);
4129 /**********************************************************************
4130 * GetMenu (USER32.@)
4132 HMENU WINAPI
GetMenu( HWND hWnd
)
4134 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4135 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4139 /**********************************************************************
4140 * GetMenuBarInfo (USER32.@)
4142 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4144 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4148 /**********************************************************************
4151 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4152 * SetWindowPos call that would result if SetMenu were called directly.
4154 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4156 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4158 if (hMenu
&& !IsMenu(hMenu
))
4160 WARN("hMenu %p is not a menu handle\n", hMenu
);
4163 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4166 hWnd
= WIN_GetFullHandle( hWnd
);
4167 if (GetCapture() == hWnd
)
4168 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4174 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4176 lpmenu
->hWnd
= hWnd
;
4177 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4179 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4184 /**********************************************************************
4185 * SetMenu (USER32.@)
4187 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4189 if(!MENU_SetMenu(hWnd
, hMenu
))
4192 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4193 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4198 /**********************************************************************
4199 * GetSubMenu (USER32.@)
4201 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4205 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4206 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4207 return lpmi
->hSubMenu
;
4211 /**********************************************************************
4212 * DrawMenuBar (USER32.@)
4214 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4217 HMENU hMenu
= GetMenu(hWnd
);
4219 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4221 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4223 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4224 lppop
->hwndOwner
= hWnd
;
4225 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4226 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4230 /***********************************************************************
4231 * DrawMenuBarTemp (USER32.@)
4235 * called by W98SE desk.cpl Control Panel Applet
4237 * Not 100% sure about the param names, but close.
4239 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4244 BOOL flat_menu
= FALSE
;
4246 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4249 hMenu
= GetMenu(hwnd
);
4252 hFont
= get_menu_font(FALSE
);
4254 lppop
= MENU_GetMenu( hMenu
);
4255 if (lppop
== NULL
|| lprect
== NULL
)
4257 retvalue
= GetSystemMetrics(SM_CYMENU
);
4261 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4263 hfontOld
= SelectObject( hDC
, hFont
);
4265 if (lppop
->Height
== 0)
4266 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4268 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4270 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4272 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4273 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4274 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4276 if (lppop
->nItems
== 0)
4278 retvalue
= GetSystemMetrics(SM_CYMENU
);
4282 for (i
= 0; i
< lppop
->nItems
; i
++)
4284 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4285 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4287 retvalue
= lppop
->Height
;
4290 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4294 /***********************************************************************
4295 * EndMenu (USER.187)
4296 * EndMenu (USER32.@)
4298 BOOL WINAPI
EndMenu(void)
4300 /* if we are in the menu code, and it is active */
4301 if (!fEndMenu
&& top_popup
)
4303 /* terminate the menu handling code */
4306 /* needs to be posted to wakeup the internal menu handler */
4307 /* which will now terminate the menu, in the event that */
4308 /* the main window was minimized, or lost focus, so we */
4309 /* don't end up with an orphaned menu */
4310 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4316 /***********************************************************************
4317 * LookupMenuHandle (USER.217)
4319 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
4321 HMENU hmenu32
= HMENU_32(hmenu
);
4323 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
4324 else return HMENU_16(hmenu32
);
4328 /**********************************************************************
4329 * LoadMenu (USER.150)
4331 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
4337 if (HIWORD(name
) && name
[0] == '#') name
= ULongToPtr(atoi( name
+ 1 ));
4338 if (!name
) return 0;
4340 instance
= GetExePtr( instance
);
4341 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
4342 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
4343 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
4344 FreeResource16( handle
);
4349 /*****************************************************************
4350 * LoadMenuA (USER32.@)
4352 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4354 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4355 if (!hrsrc
) return 0;
4356 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4360 /*****************************************************************
4361 * LoadMenuW (USER32.@)
4363 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4365 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4366 if (!hrsrc
) return 0;
4367 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4371 /**********************************************************************
4372 * LoadMenuIndirect (USER.220)
4374 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
4377 WORD version
, offset
;
4378 LPCSTR p
= template;
4380 TRACE("(%p)\n", template );
4381 version
= GET_WORD(p
);
4385 WARN("version must be 0 for Win16\n" );
4388 offset
= GET_WORD(p
);
4389 p
+= sizeof(WORD
) + offset
;
4390 if (!(hMenu
= CreateMenu())) return 0;
4391 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
4393 DestroyMenu( hMenu
);
4396 return HMENU_16(hMenu
);
4400 /**********************************************************************
4401 * LoadMenuIndirectW (USER32.@)
4403 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4406 WORD version
, offset
;
4407 LPCSTR p
= template;
4409 version
= GET_WORD(p
);
4411 TRACE("%p, ver %d\n", template, version
);
4414 case 0: /* standard format is version of 0 */
4415 offset
= GET_WORD(p
);
4416 p
+= sizeof(WORD
) + offset
;
4417 if (!(hMenu
= CreateMenu())) return 0;
4418 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4420 DestroyMenu( hMenu
);
4424 case 1: /* extended format is version of 1 */
4425 offset
= GET_WORD(p
);
4426 p
+= sizeof(WORD
) + offset
;
4427 if (!(hMenu
= CreateMenu())) return 0;
4428 if (!MENUEX_ParseResource( p
, hMenu
))
4430 DestroyMenu( hMenu
);
4435 ERR("version %d not supported.\n", version
);
4441 /**********************************************************************
4442 * LoadMenuIndirectA (USER32.@)
4444 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4446 return LoadMenuIndirectW( template );
4450 /**********************************************************************
4453 BOOL WINAPI
IsMenu(HMENU hmenu
)
4455 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4459 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4465 /**********************************************************************
4466 * GetMenuItemInfo_common
4469 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4470 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4472 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4474 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4477 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4481 if( lpmii
->fMask
& MIIM_TYPE
) {
4482 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4483 WARN("invalid combination of fMask bits used\n");
4484 /* this does not happen on Win9x/ME */
4485 SetLastError( ERROR_INVALID_PARAMETER
);
4488 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4489 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4490 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4491 if( lpmii
->fType
& MFT_BITMAP
) {
4492 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4494 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4495 /* this does not happen on Win9x/ME */
4496 lpmii
->dwTypeData
= 0;
4501 /* copy the text string */
4502 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4504 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4507 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4509 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4515 len
= strlenW(menu
->text
);
4516 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4517 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4521 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4522 0, NULL
, NULL
) - 1;
4523 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4524 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4525 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4526 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4528 /* if we've copied a substring we return its length */
4529 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4530 if (lpmii
->cch
<= len
+ 1)
4535 /* return length of string */
4536 /* not on Win9x/ME if fType & MFT_BITMAP */
4542 if (lpmii
->fMask
& MIIM_FTYPE
)
4543 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4545 if (lpmii
->fMask
& MIIM_BITMAP
)
4546 lpmii
->hbmpItem
= menu
->hbmpItem
;
4548 if (lpmii
->fMask
& MIIM_STATE
)
4549 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4551 if (lpmii
->fMask
& MIIM_ID
)
4552 lpmii
->wID
= menu
->wID
;
4554 if (lpmii
->fMask
& MIIM_SUBMENU
)
4555 lpmii
->hSubMenu
= menu
->hSubMenu
;
4557 /* hSubMenu is always cleared
4558 * (not on Win9x/ME ) */
4559 lpmii
->hSubMenu
= 0;
4562 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4563 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4564 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4566 if (lpmii
->fMask
& MIIM_DATA
)
4567 lpmii
->dwItemData
= menu
->dwItemData
;
4572 /**********************************************************************
4573 * GetMenuItemInfoA (USER32.@)
4575 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4576 LPMENUITEMINFOA lpmii
)
4580 if( lpmii
->cbSize
!= sizeof( mii
) &&
4581 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4582 SetLastError( ERROR_INVALID_PARAMETER
);
4585 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4586 mii
.cbSize
= sizeof( mii
);
4587 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4588 (LPMENUITEMINFOW
)&mii
, FALSE
);
4589 mii
.cbSize
= lpmii
->cbSize
;
4590 memcpy( lpmii
, &mii
, mii
.cbSize
);
4594 /**********************************************************************
4595 * GetMenuItemInfoW (USER32.@)
4597 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4598 LPMENUITEMINFOW lpmii
)
4602 if( lpmii
->cbSize
!= sizeof( mii
) &&
4603 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4604 SetLastError( ERROR_INVALID_PARAMETER
);
4607 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4608 mii
.cbSize
= sizeof( mii
);
4609 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4610 mii
.cbSize
= lpmii
->cbSize
;
4611 memcpy( lpmii
, &mii
, mii
.cbSize
);
4616 /* set a menu item text from a ASCII or Unicode string */
4617 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4623 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4624 strcpyW( menu
->text
, text
);
4628 LPCSTR str
= (LPCSTR
)text
;
4629 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4630 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4631 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4636 /**********************************************************************
4637 * SetMenuItemInfo_common
4640 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4641 const MENUITEMINFOW
*lpmii
,
4644 if (!menu
) return FALSE
;
4646 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4648 if (lpmii
->fMask
& MIIM_TYPE
) {
4649 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4650 WARN("invalid combination of fMask bits used\n");
4651 /* this does not happen on Win9x/ME */
4652 SetLastError( ERROR_INVALID_PARAMETER
);
4656 /* Remove the old type bits and replace them with the new ones */
4657 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4658 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4660 if (IS_STRING_ITEM(menu
->fType
)) {
4661 HeapFree(GetProcessHeap(), 0, menu
->text
);
4662 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4663 } else if( (menu
->fType
) & MFT_BITMAP
)
4664 menu
->hbmpItem
= HBITMAP_32(LOWORD(lpmii
->dwTypeData
));
4667 if (lpmii
->fMask
& MIIM_FTYPE
) {
4668 if(( lpmii
->fType
& MFT_BITMAP
)) {
4669 SetLastError( ERROR_INVALID_PARAMETER
);
4672 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4673 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4675 if (lpmii
->fMask
& MIIM_STRING
) {
4676 /* free the string when used */
4677 HeapFree(GetProcessHeap(), 0, menu
->text
);
4678 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4681 if (lpmii
->fMask
& MIIM_STATE
)
4683 /* Other menu items having MFS_DEFAULT are not converted
4685 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4688 if (lpmii
->fMask
& MIIM_ID
)
4689 menu
->wID
= lpmii
->wID
;
4691 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4692 menu
->hSubMenu
= lpmii
->hSubMenu
;
4693 if (menu
->hSubMenu
) {
4694 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4696 subMenu
->wFlags
|= MF_POPUP
;
4697 menu
->fType
|= MF_POPUP
;
4700 SetLastError( ERROR_INVALID_PARAMETER
);
4705 menu
->fType
&= ~MF_POPUP
;
4708 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4710 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4711 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4713 if (lpmii
->fMask
& MIIM_DATA
)
4714 menu
->dwItemData
= lpmii
->dwItemData
;
4716 if (lpmii
->fMask
& MIIM_BITMAP
)
4717 menu
->hbmpItem
= lpmii
->hbmpItem
;
4719 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4720 menu
->fType
|= MFT_SEPARATOR
;
4722 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4726 /**********************************************************************
4727 * SetMenuItemInfoA (USER32.@)
4729 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4730 const MENUITEMINFOA
*lpmii
)
4734 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4736 if( lpmii
->cbSize
!= sizeof( mii
) &&
4737 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4738 SetLastError( ERROR_INVALID_PARAMETER
);
4741 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4742 if( lpmii
->cbSize
!= sizeof( mii
)) {
4743 mii
.cbSize
= sizeof( mii
);
4744 mii
.hbmpItem
= NULL
;
4746 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4747 (const MENUITEMINFOW
*)&mii
, FALSE
);
4750 /**********************************************************************
4751 * SetMenuItemInfoW (USER32.@)
4753 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4754 const MENUITEMINFOW
*lpmii
)
4758 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4760 if( lpmii
->cbSize
!= sizeof( mii
) &&
4761 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4762 SetLastError( ERROR_INVALID_PARAMETER
);
4765 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4766 if( lpmii
->cbSize
!= sizeof( mii
)) {
4767 mii
.cbSize
= sizeof( mii
);
4768 mii
.hbmpItem
= NULL
;
4770 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4771 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4774 /**********************************************************************
4775 * SetMenuDefaultItem (USER32.@)
4778 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4784 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4786 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4788 /* reset all default-item flags */
4790 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4792 item
->fState
&= ~MFS_DEFAULT
;
4795 /* no default item */
4804 if ( uItem
>= menu
->nItems
) return FALSE
;
4805 item
[uItem
].fState
|= MFS_DEFAULT
;
4810 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4812 if (item
->wID
== uItem
)
4814 item
->fState
|= MFS_DEFAULT
;
4823 /**********************************************************************
4824 * GetMenuDefaultItem (USER32.@)
4826 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4832 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4834 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4836 /* find default item */
4840 if (! item
) return -1;
4842 while ( !( item
->fState
& MFS_DEFAULT
) )
4845 if (i
>= menu
->nItems
) return -1;
4848 /* default: don't return disabled items */
4849 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4851 /* search rekursiv when needed */
4852 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4855 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4856 if ( -1 != ret
) return ret
;
4858 /* when item not found in submenu, return the popup item */
4860 return ( bypos
) ? i
: item
->wID
;
4865 /**********************************************************************
4866 * InsertMenuItemA (USER32.@)
4868 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4869 const MENUITEMINFOA
*lpmii
)
4874 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4876 if( lpmii
->cbSize
!= sizeof( mii
) &&
4877 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4878 SetLastError( ERROR_INVALID_PARAMETER
);
4881 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4882 if( lpmii
->cbSize
!= sizeof( mii
)) {
4883 mii
.cbSize
= sizeof( mii
);
4884 mii
.hbmpItem
= NULL
;
4887 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4888 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)&mii
, FALSE
);
4892 /**********************************************************************
4893 * InsertMenuItemW (USER32.@)
4895 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4896 const MENUITEMINFOW
*lpmii
)
4901 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4903 if( lpmii
->cbSize
!= sizeof( mii
) &&
4904 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4905 SetLastError( ERROR_INVALID_PARAMETER
);
4908 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4909 if( lpmii
->cbSize
!= sizeof( mii
)) {
4910 mii
.cbSize
= sizeof( mii
);
4911 mii
.hbmpItem
= NULL
;
4914 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4915 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4918 /**********************************************************************
4919 * CheckMenuRadioItem (USER32.@)
4922 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4923 UINT first
, UINT last
, UINT check
,
4928 MENUITEM
*mi_first
= NULL
, *mi_check
;
4929 HMENU m_first
, m_check
;
4931 for (i
= first
; i
<= last
; i
++)
4938 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4939 if (!mi_first
) continue;
4940 mi_check
= mi_first
;
4946 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4947 if (!mi_check
) continue;
4950 if (m_first
!= m_check
) continue;
4951 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4955 mi_check
->fType
|= MFT_RADIOCHECK
;
4956 mi_check
->fState
|= MFS_CHECKED
;
4961 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4962 mi_check
->fState
&= ~MFS_CHECKED
;
4970 /**********************************************************************
4971 * GetMenuItemRect (USER32.@)
4973 * ATTENTION: Here, the returned values in rect are the screen
4974 * coordinates of the item just like if the menu was
4975 * always on the upper left side of the application.
4978 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4981 POPUPMENU
*itemMenu
;
4985 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4987 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4988 referenceHwnd
= hwnd
;
4992 itemMenu
= MENU_GetMenu(hMenu
);
4993 if (itemMenu
== NULL
)
4996 if(itemMenu
->hWnd
== 0)
4998 referenceHwnd
= itemMenu
->hWnd
;
5001 if ((rect
== NULL
) || (item
== NULL
))
5006 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
5011 /**********************************************************************
5012 * SetMenuInfo (USER32.@)
5015 * actually use the items to draw the menu
5016 * (recalculate and/or redraw)
5018 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5021 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5023 if (lpmi
->fMask
& MIM_BACKGROUND
)
5024 menu
->hbrBack
= lpmi
->hbrBack
;
5026 if (lpmi
->fMask
& MIM_HELPID
)
5027 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5029 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5030 menu
->cyMax
= lpmi
->cyMax
;
5032 if (lpmi
->fMask
& MIM_MENUDATA
)
5033 menu
->dwMenuData
= lpmi
->dwMenuData
;
5035 if (lpmi
->fMask
& MIM_STYLE
)
5036 menu
->dwStyle
= lpmi
->dwStyle
;
5038 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5040 MENUITEM
*item
= menu
->items
;
5041 for( i
= menu
->nItems
; i
; i
--, item
++)
5042 if( item
->fType
& MF_POPUP
)
5043 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5048 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5050 TRACE("(%p %p)\n", hMenu
, lpmi
);
5051 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5052 if( lpmi
->fMask
& MIM_STYLE
) {
5053 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5054 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5055 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5059 SetLastError( ERROR_INVALID_PARAMETER
);
5063 /**********************************************************************
5064 * GetMenuInfo (USER32.@)
5070 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5073 TRACE("(%p %p)\n", hMenu
, lpmi
);
5075 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5078 if (lpmi
->fMask
& MIM_BACKGROUND
)
5079 lpmi
->hbrBack
= menu
->hbrBack
;
5081 if (lpmi
->fMask
& MIM_HELPID
)
5082 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5084 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5085 lpmi
->cyMax
= menu
->cyMax
;
5087 if (lpmi
->fMask
& MIM_MENUDATA
)
5088 lpmi
->dwMenuData
= menu
->dwMenuData
;
5090 if (lpmi
->fMask
& MIM_STYLE
)
5091 lpmi
->dwStyle
= menu
->dwStyle
;
5095 SetLastError( ERROR_INVALID_PARAMETER
);
5100 /**********************************************************************
5101 * SetMenuContextHelpId (USER32.@)
5103 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5107 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5109 if ((menu
= MENU_GetMenu(hMenu
)))
5111 menu
->dwContextHelpID
= dwContextHelpID
;
5118 /**********************************************************************
5119 * GetMenuContextHelpId (USER32.@)
5121 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5125 TRACE("(%p)\n", hMenu
);
5127 if ((menu
= MENU_GetMenu(hMenu
)))
5129 return menu
->dwContextHelpID
;
5134 /**********************************************************************
5135 * MenuItemFromPoint (USER32.@)
5137 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5139 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5142 /*FIXME: Do we have to handle hWnd here? */
5143 if (!menu
) return -1;
5144 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5149 /**********************************************************************
5150 * translate_accelerator
5152 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5153 BYTE fVirt
, WORD key
, WORD cmd
)
5158 if (wParam
!= key
) return FALSE
;
5160 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5161 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5162 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5164 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5166 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5168 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5174 if(fVirt
& FVIRTKEY
)
5176 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5177 wParam
, 0xff & HIWORD(lParam
));
5179 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5180 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5184 if (!(lParam
& 0x01000000)) /* no special_key */
5186 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5187 { /* ^^ ALT pressed */
5188 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5197 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5201 HMENU hMenu
, hSubMenu
, hSysMenu
;
5202 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5204 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5205 hSysMenu
= get_win_sys_menu( hWnd
);
5207 /* find menu item and ask application to initialize it */
5208 /* 1. in the system menu */
5209 hSubMenu
= hSysMenu
;
5211 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5215 if (!IsWindowEnabled(hWnd
))
5219 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5220 if(hSubMenu
!= hSysMenu
)
5222 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5223 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5224 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5226 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5229 else /* 2. in the window's menu */
5233 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5237 if (!IsWindowEnabled(hWnd
))
5241 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5242 if(hSubMenu
!= hMenu
)
5244 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5245 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5246 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5248 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5255 if (uSysStat
!= (UINT
)-1)
5257 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5264 if (uStat
!= (UINT
)-1)
5270 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5282 if( mesg
==WM_COMMAND
)
5284 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5285 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5287 else if( mesg
==WM_SYSCOMMAND
)
5289 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5290 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5294 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5295 * #0: unknown (please report!)
5296 * #1: for WM_KEYUP,WM_SYSKEYUP
5297 * #2: mouse is captured
5298 * #3: window is disabled
5299 * #4: it's a disabled system menu option
5300 * #5: it's a menu option, but window is iconic
5301 * #6: it's a menu option, but disabled
5303 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5305 ERR_(accel
)(" unknown reason - please report!\n");
5310 /**********************************************************************
5311 * TranslateAcceleratorA (USER32.@)
5312 * TranslateAccelerator (USER32.@)
5314 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5317 LPACCEL16 lpAccelTbl
;
5321 if (!hWnd
|| !msg
) return 0;
5323 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5325 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5329 wParam
= msg
->wParam
;
5331 switch (msg
->message
)
5340 char ch
= LOWORD(wParam
);
5342 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5343 wParam
= MAKEWPARAM(wch
, HIWORD(wParam
));
5351 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5352 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5356 if (translate_accelerator( hWnd
, msg
->message
, wParam
, msg
->lParam
,
5357 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5359 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
5364 /**********************************************************************
5365 * TranslateAcceleratorW (USER32.@)
5367 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5370 LPACCEL16 lpAccelTbl
;
5373 if (!hWnd
|| !msg
) return 0;
5375 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
5377 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
5381 switch (msg
->message
)
5393 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5394 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5398 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5399 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
5401 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);