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/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
63 WINE_DECLARE_DEBUG_CHANNEL(accel
);
65 /* internal popup menu window messages */
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
70 /* Menu item structure */
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType
; /* Item type. */
74 UINT fState
; /* Item state. */
75 UINT_PTR wID
; /* Item id. */
76 HMENU hSubMenu
; /* Pop-up menu. */
77 HBITMAP hCheckBit
; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
79 LPWSTR text
; /* Item text. */
80 ULONG_PTR dwItemData
; /* Application defined. */
81 LPWSTR dwTypeData
; /* depends on fMask */
82 HBITMAP hbmpItem
; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect
; /* Item area (relative to menu window) */
85 UINT xTab
; /* X position of text after Tab */
86 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
90 /* Popup menu structure */
92 struct user_object obj
;
93 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width
; /* Width of the whole menu */
95 WORD Height
; /* Height of the whole menu */
96 UINT nItems
; /* Number of items in the menu */
97 HWND hWnd
; /* Window containing the menu */
98 MENUITEM
*items
; /* Array of menu items */
99 UINT FocusedItem
; /* Currently focused item */
100 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling
; /* Scroll arrows are active */
103 UINT nScrollPos
; /* Current scroll position */
104 UINT nTotalHeight
; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle
; /* Extended menu style */
107 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack
; /* brush for menu background */
109 DWORD dwContextHelpID
;
110 DWORD dwMenuData
; /* application defined value */
111 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
112 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU
, *LPPOPUPMENU
;
115 /* internal flags for menu tracking */
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
124 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu
; /* initial menu */
126 HWND hOwnerWnd
; /* where notifications are sent */
130 #define MENU_MAGIC 0x554d /* 'MU' */
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
175 static SIZE menucharsize
;
176 static UINT ODitemheight
; /* default owner drawn item height */
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup
;
181 static HMENU top_popup_hmenu
;
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu
= FALSE
;
186 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
188 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
190 /*********************************************************************
191 * menu class descriptor
193 const struct builtin_class_descr MENU_builtin_class
=
195 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
196 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
197 WINPROC_MENU
, /* proc */
198 sizeof(HMENU
), /* extra */
199 IDC_ARROW
, /* cursor */
200 (HBRUSH
)(COLOR_MENU
+1) /* brush */
204 /***********************************************************************
205 * debug_print_menuitem
207 * Print a menuitem in readable form.
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
216 #define MENUFLAG(bit,text) \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
221 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
224 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix
);
230 UINT flags
= mp
->fType
;
231 TRACE( "{ ID=0x%lx", mp
->wID
);
233 TRACE( ", Sub=%p", mp
->hSubMenu
);
237 MENUFLAG( MFT_SEPARATOR
, "sep");
238 MENUFLAG( MFT_OWNERDRAW
, "own");
239 MENUFLAG( MFT_BITMAP
, "bit");
240 MENUFLAG(MF_POPUP
, "pop");
241 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
242 MENUFLAG(MFT_MENUBREAK
, "brk");
243 MENUFLAG(MFT_RADIOCHECK
, "radio");
244 MENUFLAG(MFT_RIGHTORDER
, "rorder");
245 MENUFLAG(MF_SYSMENU
, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
248 TRACE( "+0x%x", flags
);
254 MENUFLAG(MFS_GRAYED
, "grey");
255 MENUFLAG(MFS_DEFAULT
, "default");
256 MENUFLAG(MFS_DISABLED
, "dis");
257 MENUFLAG(MFS_CHECKED
, "check");
258 MENUFLAG(MFS_HILITE
, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
260 MENUFLAG(MF_MOUSESELECT
, "mouse");
262 TRACE( "+0x%x", flags
);
265 TRACE( ", Chk=%p", mp
->hCheckBit
);
267 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
269 TRACE( ", Text=%s", debugstr_w(mp
->text
));
271 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
274 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
275 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
277 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
282 TRACE(" %s\n", postfix
);
289 /***********************************************************************
292 * Validate the given menu handle and returns the menu structure pointer.
294 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
296 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
298 if (menu
== OBJ_OTHER_PROCESS
)
300 WARN( "other process menu %p?\n", hMenu
);
303 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu
);
308 /***********************************************************************
311 * Get the system menu of a window
313 static HMENU
get_win_sys_menu( HWND hwnd
)
316 WND
*win
= WIN_GetPtr( hwnd
);
317 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
320 WIN_ReleasePtr( win
);
325 /***********************************************************************
328 static HFONT
get_menu_font( BOOL bold
)
330 static HFONT hMenuFont
, hMenuFontBold
;
332 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
336 NONCLIENTMETRICSW ncm
;
339 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
344 ncm
.lfMenuFont
.lfWeight
+= 300;
345 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
347 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
348 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
352 /* another thread beat us to it */
360 /***********************************************************************
363 static HBITMAP
get_arrow_bitmap(void)
365 static HBITMAP arrow_bitmap
;
367 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
371 /***********************************************************************
372 * get_down_arrow_bitmap
374 static HBITMAP
get_down_arrow_bitmap(void)
376 static HBITMAP arrow_bitmap
;
378 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW
));
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
385 static HBITMAP
get_down_arrow_inactive_bitmap(void)
387 static HBITMAP arrow_bitmap
;
389 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI
));
393 /***********************************************************************
394 * get_up_arrow_bitmap
396 static HBITMAP
get_up_arrow_bitmap(void)
398 static HBITMAP arrow_bitmap
;
400 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW
));
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
407 static HBITMAP
get_up_arrow_inactive_bitmap(void)
409 static HBITMAP arrow_bitmap
;
411 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI
));
415 /***********************************************************************
418 * Return the default system menu.
420 static HMENU
MENU_CopySysPopup(void)
422 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
423 HMENU hMenu
= LoadMenuW(user32_module
, sysmenuW
);
427 MENUITEMINFOW miteminfo
;
428 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
429 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
430 /* decorate the menu with bitmaps */
431 minfo
.cbSize
= sizeof( MENUINFO
);
432 minfo
.dwStyle
= MNS_CHECKORBMP
;
433 minfo
.fMask
= MIM_STYLE
;
434 SetMenuInfo( hMenu
, &minfo
);
435 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
436 miteminfo
.fMask
= MIIM_BITMAP
;
437 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
438 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
439 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
440 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
441 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
442 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
443 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
444 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
445 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
448 ERR("Unable to load default system menu\n" );
450 TRACE("returning %p.\n", hMenu
);
456 /**********************************************************************
459 * Create a copy of the system menu. System menu in Windows is
460 * a special menu bar with the single entry - system menu popup.
461 * This popup is presented to the outside world as a "system menu".
462 * However, the real system menu handle is sometimes seen in the
463 * WM_MENUSELECT parameters (and Word 6 likes it this way).
465 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
469 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
470 if ((hMenu
= CreateMenu()))
472 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
473 menu
->wFlags
= MF_SYSMENU
;
474 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
475 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
478 hPopupMenu
= MENU_CopySysPopup();
482 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
483 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
485 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
486 (UINT_PTR
)hPopupMenu
, NULL
);
488 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
489 menu
->items
[0].fState
= 0;
490 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
492 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
495 DestroyMenu( hMenu
);
497 ERR("failed to load system menu!\n");
502 /***********************************************************************
503 * MENU_InitSysMenuPopup
505 * Grey the appropriate items in System menu.
507 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
511 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
512 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
513 gray
= ((style
& WS_MAXIMIZE
) != 0);
514 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
515 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
516 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
517 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
518 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
519 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
520 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
521 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
523 /* The menu item must keep its state if it's disabled */
525 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
529 /******************************************************************************
531 * UINT MENU_GetStartOfNextColumn(
534 *****************************************************************************/
536 static UINT
MENU_GetStartOfNextColumn(
539 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
543 return NO_SELECTED_ITEM
;
545 i
= menu
->FocusedItem
+ 1;
546 if( i
== NO_SELECTED_ITEM
)
549 for( ; i
< menu
->nItems
; ++i
) {
550 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
554 return NO_SELECTED_ITEM
;
558 /******************************************************************************
560 * UINT MENU_GetStartOfPrevColumn(
563 *****************************************************************************/
565 static UINT
MENU_GetStartOfPrevColumn(
568 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
572 return NO_SELECTED_ITEM
;
574 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
575 return NO_SELECTED_ITEM
;
577 /* Find the start of the column */
579 for(i
= menu
->FocusedItem
; i
!= 0 &&
580 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
584 return NO_SELECTED_ITEM
;
586 for(--i
; i
!= 0; --i
) {
587 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
591 TRACE("ret %d.\n", i
);
598 /***********************************************************************
601 * Find a menu item. Return a pointer on the item, and modifies *hmenu
602 * in case the item was in a sub-menu.
604 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
607 MENUITEM
*fallback
= NULL
;
608 UINT fallback_pos
= 0;
611 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
612 if (wFlags
& MF_BYPOSITION
)
614 if (*nPos
>= menu
->nItems
) return NULL
;
615 return &menu
->items
[*nPos
];
619 MENUITEM
*item
= menu
->items
;
620 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
622 if (item
->fType
& MF_POPUP
)
624 HMENU hsubmenu
= item
->hSubMenu
;
625 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
631 else if (item
->wID
== *nPos
)
633 /* fallback to this item if nothing else found */
638 else if (item
->wID
== *nPos
)
647 *nPos
= fallback_pos
;
652 /***********************************************************************
655 * Find a Sub menu. Return the position of the submenu, and modifies
656 * *hmenu in case it is found in another sub-menu.
657 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
659 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
664 if (((*hmenu
)==(HMENU
)0xffff) ||
665 (!(menu
= MENU_GetMenu(*hmenu
))))
666 return NO_SELECTED_ITEM
;
668 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
669 if(!(item
->fType
& MF_POPUP
)) continue;
670 if (item
->hSubMenu
== hSubTarget
) {
674 HMENU hsubmenu
= item
->hSubMenu
;
675 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
676 if (pos
!= NO_SELECTED_ITEM
) {
682 return NO_SELECTED_ITEM
;
685 /***********************************************************************
688 static void MENU_FreeItemData( MENUITEM
* item
)
691 HeapFree( GetProcessHeap(), 0, item
->text
);
694 /***********************************************************************
695 * MENU_AdjustMenuItemRect
697 * Adjust menu item rectangle according to scrolling state.
700 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
702 if (menu
->bScrolling
)
704 UINT arrow_bitmap_height
;
707 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp
), &bmp
);
708 arrow_bitmap_height
= bmp
.bmHeight
;
709 rect
->top
+= arrow_bitmap_height
- menu
->nScrollPos
;
710 rect
->bottom
+= arrow_bitmap_height
- menu
->nScrollPos
;
715 /***********************************************************************
716 * MENU_FindItemByCoords
718 * Find the item at the specified coordinates (screen coords). Does
719 * not work for child windows and therefore should not be called for
720 * an arbitrary system menu.
722 static MENUITEM
*MENU_FindItemByCoords( const POPUPMENU
*menu
,
723 POINT pt
, UINT
*pos
)
729 if (!GetWindowRect(menu
->hWnd
, &rect
)) return NULL
;
733 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
736 MENU_AdjustMenuItemRect(menu
, &rect
);
737 if (PtInRect(&rect
, pt
))
747 /***********************************************************************
750 * Find the menu item selected by a key press.
751 * Return item id, -1 if none, -2 if we should close the menu.
753 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
754 WCHAR key
, BOOL forceMenuChar
)
756 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
758 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
762 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
763 MENUITEM
*item
= menu
->items
;
770 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
774 WCHAR
*p
= item
->text
- 2;
777 p
= strchrW (p
+ 2, '&');
779 while (p
!= NULL
&& p
[1] == '&');
780 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
784 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
785 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
786 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
787 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
793 /***********************************************************************
794 * MENU_GetBitmapItemSize
796 * Get the size of a bitmap item.
798 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
802 HBITMAP bmp
= lpitem
->hbmpItem
;
804 size
->cx
= size
->cy
= 0;
806 /* check if there is a magic menu item associated with this item */
807 switch( (INT_PTR
) bmp
)
809 case (INT_PTR
)HBMMENU_CALLBACK
:
811 MEASUREITEMSTRUCT measItem
;
812 measItem
.CtlType
= ODT_MENU
;
814 measItem
.itemID
= lpitem
->wID
;
815 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
816 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
817 measItem
.itemData
= lpitem
->dwItemData
;
818 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
819 size
->cx
= measItem
.itemWidth
;
820 size
->cy
= measItem
.itemHeight
;
824 case (INT_PTR
)HBMMENU_SYSTEM
:
825 if (lpitem
->dwItemData
)
827 bmp
= (HBITMAP
)lpitem
->dwItemData
;
831 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
832 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
833 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
834 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
835 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
836 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
839 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
840 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
841 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
842 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
843 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
844 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
847 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
849 size
->cx
= bm
.bmWidth
;
850 size
->cy
= bm
.bmHeight
;
854 /***********************************************************************
855 * MENU_DrawBitmapItem
857 * Draw a bitmap item.
859 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
860 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
866 int w
= rect
->right
- rect
->left
;
867 int h
= rect
->bottom
- rect
->top
;
870 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
873 /* Check if there is a magic menu item associated with this item */
874 if (IS_MAGIC_BITMAP(hbmToDraw
))
880 switch((INT_PTR
)hbmToDraw
)
882 case (INT_PTR
)HBMMENU_SYSTEM
:
883 if (lpitem
->dwItemData
)
885 bmp
= (HBITMAP
)lpitem
->dwItemData
;
886 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
890 static HBITMAP hBmpSysMenu
;
892 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
894 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
895 /* only use right half of the bitmap */
896 bmp_xoffset
= bm
.bmWidth
/ 2;
897 bm
.bmWidth
-= bmp_xoffset
;
900 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
901 flags
= DFCS_CAPTIONRESTORE
;
903 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
904 flags
= DFCS_CAPTIONMIN
;
906 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
907 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
909 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
910 flags
= DFCS_CAPTIONCLOSE
;
912 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
913 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
915 case (INT_PTR
)HBMMENU_CALLBACK
:
917 DRAWITEMSTRUCT drawItem
;
918 drawItem
.CtlType
= ODT_MENU
;
920 drawItem
.itemID
= lpitem
->wID
;
921 drawItem
.itemAction
= odaction
;
922 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
923 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
924 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
925 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
926 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
927 drawItem
.hwndItem
= (HWND
)hmenu
;
929 drawItem
.itemData
= lpitem
->dwItemData
;
930 drawItem
.rcItem
= *rect
;
931 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
935 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
938 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
941 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
944 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
948 FIXME("Magic %p not implemented\n", hbmToDraw
);
953 /* draw the magic bitmaps using marlett font characters */
954 /* FIXME: fontsize and the position (x,y) could probably be better */
955 HFONT hfont
, hfontsav
;
956 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
957 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
958 { 'M','a','r','l','e','t','t',0 } };
959 logfont
.lfHeight
= min( h
, w
) - 5 ;
960 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
961 hfont
= CreateFontIndirectW( &logfont
);
962 hfontsav
= SelectObject(hdc
, hfont
);
963 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
964 SelectObject(hdc
, hfontsav
);
965 DeleteObject( hfont
);
970 InflateRect( &r
, -1, -1 );
971 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
972 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
977 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
980 hdcMem
= CreateCompatibleDC( hdc
);
981 SelectObject( hdcMem
, bmp
);
983 /* handle fontsize > bitmap_height */
984 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
986 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
987 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
988 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
989 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
994 /***********************************************************************
997 * Calculate the size of the menu item and store it in lpitem->rect.
999 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1000 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1003 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1004 UINT arrow_bitmap_width
;
1008 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1009 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1010 (menuBar
? " (MenuBar)" : ""));
1012 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1013 arrow_bitmap_width
= bm
.bmWidth
;
1015 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1016 if( !menucharsize
.cx
) {
1017 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1018 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1019 * but it is unlikely an application will depend on that */
1020 ODitemheight
= HIWORD( GetDialogBaseUnits());
1023 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1025 if (lpitem
->fType
& MF_OWNERDRAW
)
1027 MEASUREITEMSTRUCT mis
;
1028 mis
.CtlType
= ODT_MENU
;
1030 mis
.itemID
= lpitem
->wID
;
1031 mis
.itemData
= lpitem
->dwItemData
;
1032 mis
.itemHeight
= ODitemheight
;
1034 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1035 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1036 * width of a menufont character to the width of an owner-drawn menu.
1038 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1040 /* under at least win95 you seem to be given a standard
1041 height for the menu and the height value is ignored */
1042 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1044 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1046 TRACE("id=%04lx size=%dx%d\n",
1047 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1048 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1052 if (lpitem
->fType
& MF_SEPARATOR
)
1054 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1056 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1064 if (lpitem
->hbmpItem
) {
1067 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1068 /* Keep the size of the bitmap in callback mode to be able
1069 * to draw it correctly */
1070 lpitem
->bmpsize
= size
;
1071 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1072 lpitem
->rect
.right
+= size
.cx
+ 2;
1073 itemheight
= size
.cy
+ 2;
1075 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1076 lpitem
->rect
.right
+= check_bitmap_width
;
1077 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1078 lpitem
->xTab
= lpitem
->rect
.right
;
1079 lpitem
->rect
.right
+= arrow_bitmap_width
;
1080 } else if (lpitem
->hbmpItem
) { /* menuBar */
1083 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1084 lpitem
->bmpsize
= size
;
1085 lpitem
->rect
.right
+= size
.cx
;
1086 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1087 itemheight
= size
.cy
;
1090 /* it must be a text item - unless it's the system menu */
1091 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1092 HFONT hfontOld
= NULL
;
1093 RECT rc
= lpitem
->rect
;
1094 LONG txtheight
, txtwidth
;
1096 if ( lpitem
->fState
& MFS_DEFAULT
) {
1097 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1100 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1101 DT_SINGLELINE
|DT_CALCRECT
);
1102 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1103 itemheight
= max( max( itemheight
, txtheight
),
1104 GetSystemMetrics( SM_CYMENU
) - 1);
1105 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1107 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1110 int n
= (int)( p
- lpitem
->text
);
1111 /* Item contains a tab (only meaningful in popup menus) */
1112 /* get text size before the tab */
1113 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1114 DT_SINGLELINE
|DT_CALCRECT
);
1115 txtwidth
= rc
.right
- rc
.left
;
1116 p
+= 1; /* advance past the Tab */
1117 /* get text size after the tab */
1118 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1119 DT_SINGLELINE
|DT_CALCRECT
);
1120 lpitem
->xTab
+= txtwidth
;
1121 txtheight
= max( txtheight
, tmpheight
);
1122 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1123 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1125 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1126 DT_SINGLELINE
|DT_CALCRECT
);
1127 txtwidth
= rc
.right
- rc
.left
;
1128 lpitem
->xTab
+= txtwidth
;
1130 lpitem
->rect
.right
+= 2 + txtwidth
;
1131 itemheight
= max( itemheight
,
1132 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1134 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1135 } else if( menuBar
) {
1136 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1138 lpitem
->rect
.bottom
+= itemheight
;
1139 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1143 /***********************************************************************
1144 * MENU_GetMaxPopupHeight
1147 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1150 return lppop
->cyMax
;
1151 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1155 /***********************************************************************
1156 * MENU_PopupMenuCalcSize
1158 * Calculate the size of a popup menu.
1160 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1165 int textandbmp
= FALSE
;
1166 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1168 lppop
->Width
= lppop
->Height
= 0;
1169 if (lppop
->nItems
== 0) return;
1172 SelectObject( hdc
, get_menu_font(FALSE
));
1177 lppop
->textOffset
= 0;
1179 while (start
< lppop
->nItems
)
1181 lpitem
= &lppop
->items
[start
];
1183 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1184 orgX
+= MENU_COL_SPACE
;
1185 orgY
= MENU_TOP_MARGIN
;
1187 maxTab
= maxTabWidth
= 0;
1188 /* Parse items until column break or end of menu */
1189 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1192 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1194 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1195 maxX
= max( maxX
, lpitem
->rect
.right
);
1196 orgY
= lpitem
->rect
.bottom
;
1197 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1199 maxTab
= max( maxTab
, lpitem
->xTab
);
1200 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1202 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1205 /* Finish the column (set all items to the largest width found) */
1206 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1207 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1209 lpitem
->rect
.right
= maxX
;
1210 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1211 lpitem
->xTab
= maxTab
;
1214 lppop
->Height
= max( lppop
->Height
, orgY
);
1217 lppop
->Width
= maxX
;
1218 /* if none of the items have both text and bitmap then
1219 * the text and bitmaps are all aligned on the left. If there is at
1220 * least one item with both text and bitmap then bitmaps are
1221 * on the left and texts left aligned with the right hand side
1223 if( !textandbmp
) lppop
->textOffset
= 0;
1225 /* space for 3d border */
1226 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1229 /* Adjust popup height if it exceeds maximum */
1230 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1231 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1232 if (lppop
->Height
>= maxHeight
)
1234 lppop
->Height
= maxHeight
;
1235 lppop
->bScrolling
= TRUE
;
1239 lppop
->bScrolling
= FALSE
;
1242 ReleaseDC( 0, hdc
);
1246 /***********************************************************************
1247 * MENU_MenuBarCalcSize
1249 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1250 * height is off by 1 pixel which causes lengthy window relocations when
1251 * active document window is maximized/restored.
1253 * Calculate the size of the menu bar.
1255 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1256 LPPOPUPMENU lppop
, HWND hwndOwner
)
1259 UINT start
, i
, helpPos
;
1260 int orgX
, orgY
, maxY
;
1262 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1263 if (lppop
->nItems
== 0) return;
1264 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1265 lppop
->Width
= lprect
->right
- lprect
->left
;
1267 maxY
= lprect
->top
+1;
1270 lppop
->textOffset
= 0;
1271 while (start
< lppop
->nItems
)
1273 lpitem
= &lppop
->items
[start
];
1274 orgX
= lprect
->left
;
1277 /* Parse items until line break or end of menu */
1278 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1280 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1282 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1284 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1285 debug_print_menuitem (" item: ", lpitem
, "");
1286 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1288 if (lpitem
->rect
.right
> lprect
->right
)
1290 if (i
!= start
) break;
1291 else lpitem
->rect
.right
= lprect
->right
;
1293 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1294 orgX
= lpitem
->rect
.right
;
1297 /* Finish the line (set all items to the largest height found) */
1298 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1301 lprect
->bottom
= maxY
;
1302 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1304 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1305 /* the last item (if several lines, only move the last line) */
1306 if (helpPos
== ~0U) return;
1307 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1308 orgY
= lpitem
->rect
.top
;
1309 orgX
= lprect
->right
;
1310 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1311 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1312 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1313 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1314 lpitem
->rect
.right
= orgX
;
1315 orgX
= lpitem
->rect
.left
;
1320 /***********************************************************************
1321 * MENU_DrawScrollArrows
1323 * Draw scroll arrows.
1326 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1328 HDC hdcMem
= CreateCompatibleDC(hdc
);
1329 HBITMAP hOrigBitmap
;
1330 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1334 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1335 arrow_bitmap_width
= bmp
.bmWidth
;
1336 arrow_bitmap_height
= bmp
.bmHeight
;
1339 if (lppop
->nScrollPos
)
1340 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1342 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1345 rect
.right
= lppop
->Width
;
1346 rect
.bottom
= arrow_bitmap_height
;
1347 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1348 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1349 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1350 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1351 rect
.bottom
= lppop
->Height
;
1352 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1353 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1354 SelectObject(hdcMem
, get_down_arrow_bitmap());
1356 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1357 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1358 lppop
->Height
- arrow_bitmap_height
,
1359 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1360 SelectObject(hdcMem
, hOrigBitmap
);
1365 /***********************************************************************
1368 * Draws the popup-menu arrow.
1370 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1371 UINT arrow_bitmap_height
)
1373 HDC hdcMem
= CreateCompatibleDC( hdc
);
1374 HBITMAP hOrigBitmap
;
1376 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1377 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1378 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1379 arrow_bitmap_width
, arrow_bitmap_height
,
1380 hdcMem
, 0, 0, SRCCOPY
);
1381 SelectObject( hdcMem
, hOrigBitmap
);
1384 /***********************************************************************
1387 * Draw a single menu item.
1389 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1390 UINT height
, BOOL menuBar
, UINT odaction
)
1393 BOOL flat_menu
= FALSE
;
1395 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1396 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1399 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1403 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1404 arrow_bitmap_width
= bmp
.bmWidth
;
1405 arrow_bitmap_height
= bmp
.bmHeight
;
1408 if (lpitem
->fType
& MF_SYSMENU
)
1410 if( !IsIconic(hwnd
) )
1411 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1415 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1416 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1420 if (lpitem
->fState
& MF_HILITE
)
1422 if(menuBar
&& !flat_menu
) {
1423 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1424 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1426 if(lpitem
->fState
& MF_GRAYED
)
1427 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1429 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1430 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1435 if (lpitem
->fState
& MF_GRAYED
)
1436 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1438 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1439 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1442 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1443 rect
= lpitem
->rect
;
1444 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1446 if (lpitem
->fType
& MF_OWNERDRAW
)
1449 ** Experimentation under Windows reveals that an owner-drawn
1450 ** menu is given the rectangle which includes the space it requested
1451 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1452 ** and a popup-menu arrow. This is the value of lpitem->rect.
1453 ** Windows will leave all drawing to the application except for
1454 ** the popup-menu arrow. Windows always draws that itself, after
1455 ** the menu owner has finished drawing.
1459 dis
.CtlType
= ODT_MENU
;
1461 dis
.itemID
= lpitem
->wID
;
1462 dis
.itemData
= lpitem
->dwItemData
;
1464 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1465 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1466 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1467 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1468 dis
.hwndItem
= (HWND
)hmenu
;
1471 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1472 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1473 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1474 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1475 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1476 /* Draw the popup-menu arrow */
1477 if (lpitem
->fType
& MF_POPUP
)
1478 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1479 arrow_bitmap_height
);
1483 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1485 if (lpitem
->fState
& MF_HILITE
)
1489 InflateRect (&rect
, -1, -1);
1490 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1491 InflateRect (&rect
, 1, 1);
1492 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1497 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1499 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1503 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1505 SetBkMode( hdc
, TRANSPARENT
);
1507 /* vertical separator */
1508 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1513 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1515 rc
.bottom
= height
- 3;
1518 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1519 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1520 LineTo( hdc
, rc
.left
, rc
.bottom
);
1521 SelectObject( hdc
, oldPen
);
1524 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1527 /* horizontal separator */
1528 if (lpitem
->fType
& MF_SEPARATOR
)
1535 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1538 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1539 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1540 LineTo( hdc
, rc
.right
, rc
.top
);
1541 SelectObject( hdc
, oldPen
);
1544 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1548 /* helper lines for debugging */
1549 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1550 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1551 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1552 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1555 if (lpitem
->hbmpItem
) {
1556 /* calculate the bitmap rectangle in coordinates relative
1557 * to the item rectangle */
1559 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1562 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1564 else if (menu
->dwStyle
& MNS_NOCHECK
)
1566 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1569 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1570 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1571 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1574 bmprc
.top
= (rect
.bottom
- rect
.top
-
1575 lpitem
->bmpsize
.cy
) / 2;
1576 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1582 INT y
= rect
.top
+ rect
.bottom
;
1584 int checked
= FALSE
;
1585 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1586 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1587 /* Draw the check mark
1590 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1592 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1593 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1594 lpitem
->hUnCheckBit
;
1595 if (bm
) /* we have a custom bitmap */
1597 HDC hdcMem
= CreateCompatibleDC( hdc
);
1599 SelectObject( hdcMem
, bm
);
1600 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1601 check_bitmap_width
, check_bitmap_height
,
1602 hdcMem
, 0, 0, SRCCOPY
);
1606 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1609 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1610 check_bitmap_height
, 1, 1, NULL
);
1611 HDC hdcMem
= CreateCompatibleDC( hdc
);
1613 SelectObject( hdcMem
, bm
);
1614 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1615 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1616 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1617 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1618 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1619 hdcMem
, 0, 0, SRCCOPY
);
1625 if( lpitem
->hbmpItem
&&
1626 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1628 /* some applications make this assumption on the DC's origin */
1629 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1630 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1632 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1634 /* Draw the popup-menu arrow */
1635 if (lpitem
->fType
& MF_POPUP
)
1636 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1637 arrow_bitmap_height
);
1639 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1640 rect
.left
+= check_bitmap_width
;
1641 rect
.right
-= arrow_bitmap_width
;
1643 else if( lpitem
->hbmpItem
)
1644 { /* Draw the bitmap */
1647 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1648 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1650 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1652 /* process text if present */
1658 UINT uFormat
= (menuBar
) ?
1659 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1660 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1662 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1663 rect
.left
+= menu
->textOffset
;
1665 if ( lpitem
->fState
& MFS_DEFAULT
)
1667 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1671 if( lpitem
->hbmpItem
)
1672 rect
.left
+= lpitem
->bmpsize
.cx
;
1673 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1674 rect
.left
+= menucharsize
.cx
;
1675 rect
.right
-= menucharsize
.cx
;
1678 for (i
= 0; lpitem
->text
[i
]; i
++)
1679 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1682 if(lpitem
->fState
& MF_GRAYED
)
1684 if (!(lpitem
->fState
& MF_HILITE
) )
1686 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1687 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1688 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1689 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1691 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1694 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1696 /* paint the shortcut text */
1697 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1699 if (lpitem
->text
[i
] == '\t')
1701 rect
.left
= lpitem
->xTab
;
1702 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1706 rect
.right
= lpitem
->xTab
;
1707 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1710 if(lpitem
->fState
& MF_GRAYED
)
1712 if (!(lpitem
->fState
& MF_HILITE
) )
1714 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1715 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1716 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1717 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1719 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1721 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1725 SelectObject (hdc
, hfontOld
);
1730 /***********************************************************************
1731 * MENU_DrawPopupMenu
1733 * Paint a popup menu.
1735 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1737 HBRUSH hPrevBrush
= 0;
1740 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1742 GetClientRect( hwnd
, &rect
);
1744 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1745 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1749 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1751 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1755 BOOL flat_menu
= FALSE
;
1757 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1759 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1761 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1763 if( (menu
= MENU_GetMenu( hmenu
)))
1765 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1766 /* draw menu items */
1773 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1774 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1775 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1777 /* draw scroll arrows */
1778 if (menu
->bScrolling
)
1779 MENU_DrawScrollArrows(menu
, hdc
);
1783 SelectObject( hdc
, hPrevBrush
);
1788 /***********************************************************************
1791 * Paint a menu bar. Returns the height of the menu bar.
1792 * called from [windows/nonclient.c]
1794 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1799 HMENU hMenu
= GetMenu(hwnd
);
1801 lppop
= MENU_GetMenu( hMenu
);
1802 if (lppop
== NULL
|| lprect
== NULL
)
1804 return GetSystemMetrics(SM_CYMENU
);
1809 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1811 if (lppop
->Height
== 0)
1812 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1814 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1816 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1817 return lppop
->Height
;
1820 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1824 /***********************************************************************
1827 * Display a popup menu.
1829 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1830 INT x
, INT y
, INT xanchor
, INT yanchor
)
1838 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1839 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1841 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1842 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1844 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1845 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1848 /* store the owner for DrawItem */
1849 menu
->hwndOwner
= hwndOwner
;
1851 menu
->nScrollPos
= 0;
1852 MENU_PopupMenuCalcSize( menu
);
1854 /* adjust popup menu pos so that it fits within the desktop */
1856 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1857 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1859 /* FIXME: should use item rect */
1862 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1863 info
.cbSize
= sizeof(info
);
1864 GetMonitorInfoW( monitor
, &info
);
1866 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1867 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1869 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1870 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1872 if( x
+ width
> info
.rcWork
.right
)
1874 if( xanchor
&& x
>= width
- xanchor
)
1875 x
-= width
- xanchor
;
1877 if( x
+ width
> info
.rcWork
.right
)
1878 x
= info
.rcWork
.right
- width
;
1880 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1882 if( y
+ height
> info
.rcWork
.bottom
)
1884 if( yanchor
&& y
>= height
+ yanchor
)
1885 y
-= height
+ yanchor
;
1887 if( y
+ height
> info
.rcWork
.bottom
)
1888 y
= info
.rcWork
.bottom
- height
;
1890 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1892 /* NOTE: In Windows, top menu popup is not owned. */
1893 menu
->hWnd
= CreateWindowExW( 0, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1894 WS_POPUP
, x
, y
, width
, height
,
1895 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1897 if( !menu
->hWnd
) return FALSE
;
1899 top_popup
= menu
->hWnd
;
1900 top_popup_hmenu
= hmenu
;
1902 /* Display the window */
1904 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1905 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1906 UpdateWindow( menu
->hWnd
);
1911 /***********************************************************************
1912 * MENU_EnsureMenuItemVisible
1915 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1917 if (lppop
->bScrolling
)
1919 MENUITEM
*item
= &lppop
->items
[wIndex
];
1920 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1921 UINT nOldPos
= lppop
->nScrollPos
;
1923 UINT arrow_bitmap_height
;
1926 GetClientRect(lppop
->hWnd
, &rc
);
1928 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1929 arrow_bitmap_height
= bmp
.bmHeight
;
1931 rc
.top
+= arrow_bitmap_height
;
1932 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1934 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1935 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1938 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1939 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1940 MENU_DrawScrollArrows(lppop
, hdc
);
1942 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1944 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1945 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1946 MENU_DrawScrollArrows(lppop
, hdc
);
1952 /***********************************************************************
1955 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1956 BOOL sendMenuSelect
, HMENU topmenu
)
1961 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1963 lppop
= MENU_GetMenu( hmenu
);
1964 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1966 if (lppop
->FocusedItem
== wIndex
) return;
1967 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1968 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1970 top_popup
= lppop
->hWnd
;
1971 top_popup_hmenu
= hmenu
;
1974 SelectObject( hdc
, get_menu_font(FALSE
));
1976 /* Clear previous highlighted item */
1977 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1979 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1980 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1981 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1985 /* Highlight new item (if any) */
1986 lppop
->FocusedItem
= wIndex
;
1987 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1989 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1990 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1991 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
1992 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1993 &lppop
->items
[wIndex
], lppop
->Height
,
1994 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1998 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1999 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2000 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2001 ip
->fType
| ip
->fState
|
2002 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2005 else if (sendMenuSelect
) {
2008 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2009 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2010 MENUITEM
*ip
= &ptm
->items
[pos
];
2011 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2012 ip
->fType
| ip
->fState
|
2013 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2017 ReleaseDC( lppop
->hWnd
, hdc
);
2021 /***********************************************************************
2022 * MENU_MoveSelection
2024 * Moves currently selected item according to the offset parameter.
2025 * If there is no selection then it should select the last item if
2026 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2028 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2033 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2035 menu
= MENU_GetMenu( hmenu
);
2036 if ((!menu
) || (!menu
->items
)) return;
2038 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2040 if( menu
->nItems
== 1 ) return; else
2041 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2043 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2045 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2050 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2051 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2052 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2054 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2060 /**********************************************************************
2063 * Insert (allocate) a new item into a menu.
2065 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2070 if (!(menu
= MENU_GetMenu(hMenu
)))
2073 /* Find where to insert new item */
2075 if (flags
& MF_BYPOSITION
) {
2076 if (pos
> menu
->nItems
)
2079 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2082 if (!(menu
= MENU_GetMenu( hMenu
)))
2087 /* Make sure that MDI system buttons stay on the right side.
2088 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2089 * regardless of their id.
2091 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2092 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2095 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2097 /* Create new items array */
2099 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2102 WARN("allocation failed\n" );
2105 if (menu
->nItems
> 0)
2107 /* Copy the old array into the new one */
2108 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2109 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2110 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2111 HeapFree( GetProcessHeap(), 0, menu
->items
);
2113 menu
->items
= newItems
;
2115 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2116 menu
->Height
= 0; /* force size recalculate */
2117 return &newItems
[pos
];
2121 /**********************************************************************
2122 * MENU_ParseResource
2124 * Parse a standard menu resource and add items to the menu.
2125 * Return a pointer to the end of the resource.
2127 * NOTE: flags is equivalent to the mtOption field
2129 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2137 flags
= GET_WORD(res
);
2138 end_flag
= flags
& MF_END
;
2139 /* Remove MF_END because it has the same value as MF_HILITE */
2141 res
+= sizeof(WORD
);
2142 if (!(flags
& MF_POPUP
))
2145 res
+= sizeof(WORD
);
2148 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2149 if (flags
& MF_POPUP
)
2151 HMENU hSubMenu
= CreatePopupMenu();
2152 if (!hSubMenu
) return NULL
;
2153 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2154 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2156 else /* Not a popup */
2158 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2160 } while (!end_flag
);
2165 /**********************************************************************
2166 * MENUEX_ParseResource
2168 * Parse an extended menu resource and add items to the menu.
2169 * Return a pointer to the end of the resource.
2171 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2177 mii
.cbSize
= sizeof(mii
);
2178 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2179 mii
.fType
= GET_DWORD(res
);
2180 res
+= sizeof(DWORD
);
2181 mii
.fState
= GET_DWORD(res
);
2182 res
+= sizeof(DWORD
);
2183 mii
.wID
= GET_DWORD(res
);
2184 res
+= sizeof(DWORD
);
2185 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2186 res
+= sizeof(WORD
);
2187 /* Align the text on a word boundary. */
2188 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2189 mii
.dwTypeData
= (LPWSTR
) res
;
2190 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2191 /* Align the following fields on a dword boundary. */
2192 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2194 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2195 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2197 if (resinfo
& 1) { /* Pop-up? */
2198 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2199 res
+= sizeof(DWORD
);
2200 mii
.hSubMenu
= CreatePopupMenu();
2203 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2204 DestroyMenu(mii
.hSubMenu
);
2207 mii
.fMask
|= MIIM_SUBMENU
;
2208 mii
.fType
|= MF_POPUP
;
2210 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2212 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2213 mii
.wID
, mii
.fType
);
2214 mii
.fType
|= MF_SEPARATOR
;
2216 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2217 } while (!(resinfo
& MF_END
));
2222 /***********************************************************************
2225 * Return the handle of the selected sub-popup menu (if any).
2227 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2232 menu
= MENU_GetMenu( hmenu
);
2234 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2236 item
= &menu
->items
[menu
->FocusedItem
];
2237 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2238 return item
->hSubMenu
;
2243 /***********************************************************************
2244 * MENU_HideSubPopups
2246 * Hide the sub-popup menus of this menu.
2248 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2249 BOOL sendMenuSelect
, UINT wFlags
)
2251 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2253 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2255 if (menu
&& top_popup
)
2261 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2263 item
= &menu
->items
[menu
->FocusedItem
];
2264 if (!(item
->fType
& MF_POPUP
) ||
2265 !(item
->fState
& MF_MOUSESELECT
)) return;
2266 item
->fState
&= ~MF_MOUSESELECT
;
2267 hsubmenu
= item
->hSubMenu
;
2270 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2271 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2272 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2273 DestroyWindow( submenu
->hWnd
);
2276 if (!(wFlags
& TPM_NONOTIFY
))
2277 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2278 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2283 /***********************************************************************
2286 * Display the sub-menu of the selected item of this menu.
2287 * Return the handle of the submenu, or hmenu if no submenu to display.
2289 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2290 BOOL selectFirst
, UINT wFlags
)
2297 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2299 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2301 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2303 item
= &menu
->items
[menu
->FocusedItem
];
2304 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2307 /* message must be sent before using item,
2308 because nearly everything may be changed by the application ! */
2310 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2311 if (!(wFlags
& TPM_NONOTIFY
))
2312 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2313 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2315 item
= &menu
->items
[menu
->FocusedItem
];
2318 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2319 if (!(item
->fState
& MF_HILITE
))
2321 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2322 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2324 SelectObject( hdc
, get_menu_font(FALSE
));
2326 item
->fState
|= MF_HILITE
;
2327 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2328 ReleaseDC( menu
->hWnd
, hdc
);
2330 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2333 item
->fState
|= MF_MOUSESELECT
;
2335 if (IS_SYSTEM_MENU(menu
))
2337 MENU_InitSysMenuPopup(item
->hSubMenu
,
2338 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2339 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2341 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2342 rect
.top
= rect
.bottom
;
2343 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2344 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2348 GetWindowRect( menu
->hWnd
, &rect
);
2349 if (menu
->wFlags
& MF_POPUP
)
2351 RECT rc
= item
->rect
;
2353 MENU_AdjustMenuItemRect(menu
, &rc
);
2355 /* The first item in the popup menu has to be at the
2356 same y position as the focused menu item */
2357 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2358 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2359 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2360 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2361 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2365 rect
.left
+= item
->rect
.left
;
2366 rect
.top
+= item
->rect
.bottom
;
2367 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2368 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2372 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, 0,
2373 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2375 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2376 return item
->hSubMenu
;
2381 /**********************************************************************
2384 HWND
MENU_IsMenuActive(void)
2389 /**********************************************************************
2392 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2394 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2396 void MENU_EndMenu( HWND hwnd
)
2399 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2400 if (menu
&& hwnd
== menu
->hwndOwner
) EndMenu();
2403 /***********************************************************************
2406 * Walks menu chain trying to find a menu pt maps to.
2408 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2410 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2411 UINT item
= menu
->FocusedItem
;
2414 /* try subpopup first (if any) */
2415 ret
= (item
!= NO_SELECTED_ITEM
&&
2416 (menu
->items
[item
].fType
& MF_POPUP
) &&
2417 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2418 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2420 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2422 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2423 if( menu
->wFlags
& MF_POPUP
)
2425 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2427 else if (ht
== HTSYSMENU
)
2428 ret
= get_win_sys_menu( menu
->hWnd
);
2429 else if (ht
== HTMENU
)
2430 ret
= GetMenu( menu
->hWnd
);
2435 /***********************************************************************
2436 * MENU_ExecFocusedItem
2438 * Execute a menu item (for instance when user pressed Enter).
2439 * Return the wID of the executed item. Otherwise, -1 indicating
2440 * that no menu item was executed, -2 if a popup is shown;
2441 * Have to receive the flags for the TrackPopupMenu options to avoid
2442 * sending unwanted message.
2445 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2448 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2450 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2452 if (!menu
|| !menu
->nItems
||
2453 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2455 item
= &menu
->items
[menu
->FocusedItem
];
2457 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2459 if (!(item
->fType
& MF_POPUP
))
2461 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2463 /* If TPM_RETURNCMD is set you return the id, but
2464 do not send a message to the owner */
2465 if(!(wFlags
& TPM_RETURNCMD
))
2467 if( menu
->wFlags
& MF_SYSMENU
)
2468 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2469 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2472 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2473 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2475 if (dwStyle
& MNS_NOTIFYBYPOS
)
2476 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2479 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2487 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2494 /***********************************************************************
2495 * MENU_SwitchTracking
2497 * Helper function for menu navigation routines.
2499 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2501 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2502 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2504 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2506 if( pmt
->hTopMenu
!= hPtMenu
&&
2507 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2509 /* both are top level menus (system and menu-bar) */
2510 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2511 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2512 pmt
->hTopMenu
= hPtMenu
;
2514 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2515 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2519 /***********************************************************************
2522 * Return TRUE if we can go on with menu tracking.
2524 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2526 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2531 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2534 if( IS_SYSTEM_MENU(ptmenu
) )
2535 item
= ptmenu
->items
;
2537 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2541 if( ptmenu
->FocusedItem
!= id
)
2542 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2544 /* If the popup menu is not already "popped" */
2545 if(!(item
->fState
& MF_MOUSESELECT
))
2547 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2552 /* Else the click was on the menu bar, finish the tracking */
2557 /***********************************************************************
2560 * Return the value of MENU_ExecFocusedItem if
2561 * the selected item was not a popup. Else open the popup.
2562 * A -1 return value indicates that we go on with menu tracking.
2565 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2567 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2572 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2575 if( IS_SYSTEM_MENU(ptmenu
) )
2576 item
= ptmenu
->items
;
2578 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2580 if( item
&& (ptmenu
->FocusedItem
== id
))
2582 debug_print_menuitem ("FocusedItem: ", item
, "");
2584 if( !(item
->fType
& MF_POPUP
) )
2586 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2587 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2588 return executedMenuId
;
2591 /* If we are dealing with the top-level menu */
2592 /* and this is a click on an already "popped" item: */
2593 /* Stop the menu tracking and close the opened submenus */
2594 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2597 ptmenu
->bTimeToHide
= TRUE
;
2603 /***********************************************************************
2606 * Return TRUE if we can go on with menu tracking.
2608 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2610 UINT id
= NO_SELECTED_ITEM
;
2611 POPUPMENU
*ptmenu
= NULL
;
2615 ptmenu
= MENU_GetMenu( hPtMenu
);
2616 if( IS_SYSTEM_MENU(ptmenu
) )
2619 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2622 if( id
== NO_SELECTED_ITEM
)
2624 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2625 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2628 else if( ptmenu
->FocusedItem
!= id
)
2630 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2631 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2637 /***********************************************************************
2640 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2642 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2644 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2647 /* When skipping left, we need to do something special after the
2649 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2653 /* When skipping right, for the non-system menu, we need to
2654 handle the last non-special menu item (ie skip any window
2655 icons such as MDI maximize, restore or close) */
2656 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2658 UINT i
= menu
->FocusedItem
+ 1;
2659 while (i
< menu
->nItems
) {
2660 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2661 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2665 if (i
== menu
->nItems
) {
2669 /* When skipping right, we need to cater for the system menu */
2670 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2672 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2679 MDINEXTMENU next_menu
;
2684 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2685 next_menu
.hmenuNext
= 0;
2686 next_menu
.hwndNext
= 0;
2687 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2689 TRACE("%p [%p] -> %p [%p]\n",
2690 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2692 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2694 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2695 hNewWnd
= pmt
->hOwnerWnd
;
2696 if( IS_SYSTEM_MENU(menu
) )
2698 /* switch to the menu bar */
2700 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2704 menu
= MENU_GetMenu( hNewMenu
);
2705 id
= menu
->nItems
- 1;
2707 /* Skip backwards over any system predefined icons,
2708 eg. MDI close, restore etc icons */
2710 (menu
->items
[id
].wID
>= SC_SIZE
&&
2711 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2714 else if (style
& WS_SYSMENU
)
2716 /* switch to the system menu */
2717 hNewMenu
= get_win_sys_menu( hNewWnd
);
2721 else /* application returned a new menu to switch to */
2723 hNewMenu
= next_menu
.hmenuNext
;
2724 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2726 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2728 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2730 if (style
& WS_SYSMENU
&&
2731 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2733 /* get the real system menu */
2734 hNewMenu
= get_win_sys_menu(hNewWnd
);
2736 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2738 /* FIXME: Not sure what to do here;
2739 * perhaps try to track hNewMenu as a popup? */
2741 TRACE(" -- got confused.\n");
2748 if( hNewMenu
!= pmt
->hTopMenu
)
2750 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2752 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2753 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2756 if( hNewWnd
!= pmt
->hOwnerWnd
)
2758 pmt
->hOwnerWnd
= hNewWnd
;
2759 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2762 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2763 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2770 /***********************************************************************
2773 * The idea is not to show the popup if the next input message is
2774 * going to hide it anyway.
2776 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2780 msg
.hwnd
= pmt
->hOwnerWnd
;
2782 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2783 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2788 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2789 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2791 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2792 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2793 if( msg
.message
== WM_KEYDOWN
&&
2794 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2796 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2803 /* failures go through this */
2804 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2808 /***********************************************************************
2811 * Handle a VK_ESCAPE key event in a menu.
2813 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2815 BOOL bEndMenu
= TRUE
;
2817 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2819 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2821 if (menu
->wFlags
& MF_POPUP
)
2823 HMENU hmenutmp
, hmenuprev
;
2825 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2827 /* close topmost popup */
2828 while (hmenutmp
!= pmt
->hCurrentMenu
)
2830 hmenuprev
= hmenutmp
;
2831 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2834 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2835 pmt
->hCurrentMenu
= hmenuprev
;
2843 /***********************************************************************
2846 * Handle a VK_LEFT key event in a menu.
2848 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2851 HMENU hmenutmp
, hmenuprev
;
2854 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2855 menu
= MENU_GetMenu( hmenutmp
);
2857 /* Try to move 1 column left (if possible) */
2858 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2859 NO_SELECTED_ITEM
) {
2861 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2866 /* close topmost popup */
2867 while (hmenutmp
!= pmt
->hCurrentMenu
)
2869 hmenuprev
= hmenutmp
;
2870 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2873 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2874 pmt
->hCurrentMenu
= hmenuprev
;
2876 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2878 /* move menu bar selection if no more popups are left */
2880 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2881 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2883 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2885 /* A sublevel menu was displayed - display the next one
2886 * unless there is another displacement coming up */
2888 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2889 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2890 pmt
->hTopMenu
, TRUE
, wFlags
);
2896 /***********************************************************************
2899 * Handle a VK_RIGHT key event in a menu.
2901 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2904 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2907 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2909 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2910 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2912 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2914 /* If already displaying a popup, try to display sub-popup */
2916 hmenutmp
= pmt
->hCurrentMenu
;
2917 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2919 /* if subpopup was displayed then we are done */
2920 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2923 /* Check to see if there's another column */
2924 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2925 NO_SELECTED_ITEM
) {
2926 TRACE("Going to %d.\n", nextcol
);
2927 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2932 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2934 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2936 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2937 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2938 } else hmenutmp
= 0;
2940 /* try to move to the next item */
2941 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2942 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2944 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2945 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2946 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2947 pmt
->hTopMenu
, TRUE
, wFlags
);
2951 static void CALLBACK
release_capture( BOOL __normal
)
2953 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2956 /***********************************************************************
2959 * Menu tracking code.
2961 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2962 HWND hwnd
, const RECT
*lprect
)
2967 INT executedMenuId
= -1;
2969 BOOL enterIdleSent
= FALSE
;
2973 mt
.hCurrentMenu
= hmenu
;
2974 mt
.hTopMenu
= hmenu
;
2975 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2979 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2980 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2983 if (!(menu
= MENU_GetMenu( hmenu
)))
2985 WARN("Invalid menu handle %p\n", hmenu
);
2986 SetLastError(ERROR_INVALID_MENU_HANDLE
);
2990 if (wFlags
& TPM_BUTTONDOWN
)
2992 /* Get the result in order to start the tracking or not */
2993 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2994 fEndMenu
= !fRemove
;
2997 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
2999 /* owner may not be visible when tracking a popup, so use the menu itself */
3000 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3001 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3003 __TRY
while (!fEndMenu
)
3005 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3006 if (!menu
) /* sometimes happens if I do a window manager close */
3009 /* we have to keep the message in the queue until it's
3010 * clear that menu loop is not over yet. */
3014 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3016 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3017 /* remove the message from the queue */
3018 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3024 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3025 enterIdleSent
= TRUE
;
3026 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3032 /* check if EndMenu() tried to cancel us, by posting this message */
3033 if(msg
.message
== WM_CANCELMODE
)
3035 /* we are now out of the loop */
3038 /* remove the message from the queue */
3039 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3041 /* break out of internal loop, ala ESCAPE */
3045 TranslateMessage( &msg
);
3048 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3049 enterIdleSent
=FALSE
;
3052 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3055 * Use the mouse coordinates in lParam instead of those in the MSG
3056 * struct to properly handle synthetic messages. They are already
3057 * in screen coordinates.
3059 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3060 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3062 /* Find a menu for this mouse event */
3063 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3067 /* no WM_NC... messages in captured state */
3069 case WM_RBUTTONDBLCLK
:
3070 case WM_RBUTTONDOWN
:
3071 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3073 case WM_LBUTTONDBLCLK
:
3074 case WM_LBUTTONDOWN
:
3075 /* If the message belongs to the menu, removes it from the queue */
3076 /* Else, end menu tracking */
3077 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3078 fEndMenu
= !fRemove
;
3082 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3085 /* Check if a menu was selected by the mouse */
3088 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3089 TRACE("executedMenuId %d\n", executedMenuId
);
3091 /* End the loop if executedMenuId is an item ID */
3092 /* or if the job was done (executedMenuId = 0). */
3093 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3095 /* No menu was selected by the mouse */
3096 /* if the function was called by TrackPopupMenu, continue
3097 with the menu tracking. If not, stop it */
3099 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3104 /* the selected menu item must be changed every time */
3105 /* the mouse moves. */
3108 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3110 } /* switch(msg.message) - mouse */
3112 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3114 fRemove
= TRUE
; /* Keyboard messages are always removed */
3127 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3128 NO_SELECTED_ITEM
, FALSE
, 0 );
3129 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3130 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3134 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3136 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3137 if (!(menu
->wFlags
& MF_POPUP
))
3138 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3139 else /* otherwise try to move selection */
3140 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3141 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3145 MENU_KeyLeft( &mt
, wFlags
);
3149 MENU_KeyRight( &mt
, wFlags
);
3153 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3159 hi
.cbSize
= sizeof(HELPINFO
);
3160 hi
.iContextType
= HELPINFO_MENUITEM
;
3161 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3164 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3165 hi
.hItemHandle
= hmenu
;
3166 hi
.dwContextId
= menu
->dwContextHelpID
;
3167 hi
.MousePos
= msg
.pt
;
3168 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3175 break; /* WM_KEYDOWN */
3182 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3184 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3185 fEndMenu
= (executedMenuId
!= -2);
3190 /* Hack to avoid control chars. */
3191 /* We will find a better way real soon... */
3192 if (msg
.wParam
< 32) break;
3194 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3195 LOWORD(msg
.wParam
), FALSE
);
3196 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3197 else if (pos
== (UINT
)-1) MessageBeep(0);
3200 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3202 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3203 fEndMenu
= (executedMenuId
!= -2);
3207 } /* switch(msg.message) - kbd */
3211 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3212 DispatchMessageW( &msg
);
3216 if (!fEndMenu
) fRemove
= TRUE
;
3218 /* finally remove message from the queue */
3220 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3221 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3222 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3224 __FINALLY( release_capture
)
3226 /* If dropdown is still painted and the close box is clicked on
3227 then the menu will be destroyed as part of the DispatchMessage above.
3228 This will then invalidate the menu handle in mt.hTopMenu. We should
3229 check for this first. */
3230 if( IsMenu( mt
.hTopMenu
) )
3232 menu
= MENU_GetMenu( mt
.hTopMenu
);
3234 if( IsWindow( mt
.hOwnerWnd
) )
3236 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3238 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3240 DestroyWindow( menu
->hWnd
);
3243 if (!(wFlags
& TPM_NONOTIFY
))
3244 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3245 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3247 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3248 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3251 /* Reset the variable for hiding menu */
3252 if( menu
) menu
->bTimeToHide
= FALSE
;
3255 /* The return value is only used by TrackPopupMenu */
3256 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3257 if (executedMenuId
== -1) executedMenuId
= 0;
3258 return executedMenuId
;
3261 /***********************************************************************
3264 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3268 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3272 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3273 if (!(wFlags
& TPM_NONOTIFY
))
3274 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3276 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3278 if (!(wFlags
& TPM_NONOTIFY
))
3280 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3281 /* If an app changed/recreated menu bar entries in WM_INITMENU
3282 * menu sizes will be recalculated once the menu created/shown.
3286 /* This makes the menus of applications built with Delphi work.
3287 * It also enables menus to be displayed in more than one window,
3288 * but there are some bugs left that need to be fixed in this case.
3290 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3294 /***********************************************************************
3297 static BOOL
MENU_ExitTracking(HWND hWnd
)
3299 TRACE("hwnd=%p\n", hWnd
);
3301 SendMessageW( hWnd
, WM_EXITMENULOOP
, 0, 0 );
3304 top_popup_hmenu
= NULL
;
3308 /***********************************************************************
3309 * MENU_TrackMouseMenuBar
3311 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3313 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3315 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3316 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3318 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3322 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3323 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3324 MENU_ExitTracking(hWnd
);
3329 /***********************************************************************
3330 * MENU_TrackKbdMenuBar
3332 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3334 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3336 UINT uItem
= NO_SELECTED_ITEM
;
3338 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3340 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3342 /* find window that has a menu */
3344 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3345 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3347 /* check if we have to track a system menu */
3349 hTrackMenu
= GetMenu( hwnd
);
3350 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3352 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3353 hTrackMenu
= get_win_sys_menu( hwnd
);
3355 wParam
|= HTSYSMENU
; /* prevent item lookup */
3358 if (!IsMenu( hTrackMenu
)) return;
3360 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3362 if( wChar
&& wChar
!= ' ' )
3364 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3365 if ( uItem
>= (UINT
)(-2) )
3367 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3368 /* schedule end of menu tracking */
3369 wFlags
|= TF_ENDMENU
;
3374 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3376 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3378 if( uItem
== NO_SELECTED_ITEM
)
3379 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3381 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3385 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3386 MENU_ExitTracking( hwnd
);
3389 /**********************************************************************
3390 * TrackPopupMenuEx (USER32.@)
3392 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3393 HWND hWnd
, LPTPMPARAMS lpTpm
)
3397 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3398 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3399 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3401 /* Parameter check */
3402 /* FIXME: this check is performed several times, here and in the called
3403 functions. That could be optimized */
3404 if (!MENU_GetMenu( hMenu
))
3406 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3410 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3412 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3413 if (!(wFlags
& TPM_NONOTIFY
))
3414 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3416 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3417 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3418 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3419 MENU_ExitTracking(hWnd
);
3424 /**********************************************************************
3425 * TrackPopupMenu (USER32.@)
3427 * Like the win32 API, the function return the command ID only if the
3428 * flag TPM_RETURNCMD is on.
3431 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3432 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3434 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3437 /***********************************************************************
3440 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3442 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3444 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3450 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3451 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3455 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3456 return MA_NOACTIVATE
;
3461 BeginPaint( hwnd
, &ps
);
3462 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3463 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3464 EndPaint( hwnd
, &ps
);
3468 case WM_PRINTCLIENT
:
3470 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3471 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3479 /* zero out global pointer in case resident popup window was destroyed. */
3480 if (hwnd
== top_popup
) {
3482 top_popup_hmenu
= NULL
;
3490 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3493 SetWindowLongPtrW( hwnd
, 0, 0 );
3496 case MM_SETMENUHANDLE
:
3497 SetWindowLongPtrW( hwnd
, 0, wParam
);
3500 case MM_GETMENUHANDLE
:
3502 return GetWindowLongPtrW( hwnd
, 0 );
3505 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3511 /***********************************************************************
3512 * MENU_GetMenuBarHeight
3514 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3516 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3517 INT orgX
, INT orgY
)
3523 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3525 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3527 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3528 SelectObject( hdc
, get_menu_font(FALSE
));
3529 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3530 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3531 ReleaseDC( hwnd
, hdc
);
3532 return lppop
->Height
;
3536 /*******************************************************************
3537 * ChangeMenuA (USER32.@)
3539 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3540 UINT id
, UINT flags
)
3542 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3543 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3545 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3546 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3548 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3549 flags
& MF_BYPOSITION
? pos
: id
,
3550 flags
& ~MF_REMOVE
);
3551 /* Default: MF_INSERT */
3552 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3556 /*******************************************************************
3557 * ChangeMenuW (USER32.@)
3559 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3560 UINT id
, UINT flags
)
3562 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3563 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3565 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3566 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3568 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3569 flags
& MF_BYPOSITION
? pos
: id
,
3570 flags
& ~MF_REMOVE
);
3571 /* Default: MF_INSERT */
3572 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3576 /*******************************************************************
3577 * CheckMenuItem (USER32.@)
3579 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3584 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3585 ret
= item
->fState
& MF_CHECKED
;
3586 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3587 else item
->fState
&= ~MF_CHECKED
;
3592 /**********************************************************************
3593 * EnableMenuItem (USER32.@)
3595 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3601 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3603 /* Get the Popupmenu to access the owner menu */
3604 if (!(menu
= MENU_GetMenu(hMenu
)))
3607 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3610 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3611 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3613 /* If the close item in the system menu change update the close button */
3614 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3616 if (menu
->hSysMenuOwner
!= 0)
3619 POPUPMENU
* parentMenu
;
3621 /* Get the parent menu to access*/
3622 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3625 /* Refresh the frame to reflect the change */
3626 GetWindowRect(parentMenu
->hWnd
, &rc
);
3627 MapWindowPoints(0, parentMenu
->hWnd
, (POINT
*)&rc
, 2);
3629 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3637 /*******************************************************************
3638 * GetMenuStringA (USER32.@)
3640 INT WINAPI
GetMenuStringA(
3641 HMENU hMenu
, /* [in] menuhandle */
3642 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3643 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3644 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3645 UINT wFlags
/* [in] MF_ flags */
3649 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3650 if (str
&& nMaxSiz
) str
[0] = '\0';
3651 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3652 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3655 if (!item
->text
) return 0;
3656 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3657 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3659 TRACE("returning %s\n", debugstr_a(str
));
3664 /*******************************************************************
3665 * GetMenuStringW (USER32.@)
3667 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3668 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3672 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3673 if (str
&& nMaxSiz
) str
[0] = '\0';
3674 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3675 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3678 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3679 if( !(item
->text
)) {
3683 lstrcpynW( str
, item
->text
, nMaxSiz
);
3684 TRACE("returning %s\n", debugstr_w(str
));
3685 return strlenW(str
);
3689 /**********************************************************************
3690 * HiliteMenuItem (USER32.@)
3692 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3696 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3697 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3698 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3699 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3700 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3701 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3706 /**********************************************************************
3707 * GetMenuState (USER32.@)
3709 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3712 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3713 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3714 debug_print_menuitem (" item: ", item
, "");
3715 if (item
->fType
& MF_POPUP
)
3717 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3718 if (!menu
) return -1;
3719 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3723 /* We used to (from way back then) mask the result to 0xff. */
3724 /* I don't know why and it seems wrong as the documented */
3725 /* return flag MF_SEPARATOR is outside that mask. */
3726 return (item
->fType
| item
->fState
);
3731 /**********************************************************************
3732 * GetMenuItemCount (USER32.@)
3734 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3736 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3737 if (!menu
) return -1;
3738 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3739 return menu
->nItems
;
3743 /**********************************************************************
3744 * GetMenuItemID (USER32.@)
3746 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3750 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3751 if (lpmi
->fType
& MF_POPUP
) return -1;
3757 /**********************************************************************
3760 * Uses flags, id and text ptr, passed by InsertMenu() and
3761 * ModifyMenu() to setup a MenuItemInfo structure.
3763 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3764 LPMENUITEMINFOW pmii
)
3766 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3767 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3768 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3769 /* setting bitmap clears text and vice versa */
3770 if( IS_STRING_ITEM(flags
)) {
3771 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3773 flags
|= MF_SEPARATOR
;
3774 /* Item beginning with a backspace is a help item */
3775 /* FIXME: wrong place, this is only true in win16 */
3776 else if( *str
== '\b') {
3780 pmii
->dwTypeData
= (LPWSTR
)str
;
3781 } else if( flags
& MFT_BITMAP
){
3782 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3783 pmii
->hbmpItem
= ULongToHandle(LOWORD(str
));
3785 if( flags
& MF_OWNERDRAW
){
3786 pmii
->fMask
|= MIIM_DATA
;
3787 pmii
->dwItemData
= (ULONG_PTR
) str
;
3789 if( flags
& MF_POPUP
) {
3790 pmii
->fMask
|= MIIM_SUBMENU
;
3791 pmii
->hSubMenu
= (HMENU
)id
;
3793 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3794 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3795 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3796 pmii
->wID
= (UINT
)id
;
3800 /*******************************************************************
3801 * InsertMenuW (USER32.@)
3803 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3804 UINT_PTR id
, LPCWSTR str
)
3809 if (IS_STRING_ITEM(flags
) && str
)
3810 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3811 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3812 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3813 hMenu
, pos
, flags
, id
, str
);
3815 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3816 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3817 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3819 RemoveMenu( hMenu
, pos
, flags
);
3823 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3828 /*******************************************************************
3829 * InsertMenuA (USER32.@)
3831 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3832 UINT_PTR id
, LPCSTR str
)
3836 if (IS_STRING_ITEM(flags
) && str
)
3838 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3839 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3842 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3843 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3844 HeapFree( GetProcessHeap(), 0, newstr
);
3848 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3852 /*******************************************************************
3853 * AppendMenuA (USER32.@)
3855 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3856 UINT_PTR id
, LPCSTR data
)
3858 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3862 /*******************************************************************
3863 * AppendMenuW (USER32.@)
3865 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3866 UINT_PTR id
, LPCWSTR data
)
3868 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3872 /**********************************************************************
3873 * RemoveMenu (USER32.@)
3875 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3880 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3881 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3882 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3886 MENU_FreeItemData( item
);
3888 if (--menu
->nItems
== 0)
3890 HeapFree( GetProcessHeap(), 0, menu
->items
);
3895 while(nPos
< menu
->nItems
)
3901 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3902 menu
->nItems
* sizeof(MENUITEM
) );
3908 /**********************************************************************
3909 * DeleteMenu (USER32.@)
3911 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3913 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3914 if (!item
) return FALSE
;
3915 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3916 /* nPos is now the position of the item */
3917 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3922 /*******************************************************************
3923 * ModifyMenuW (USER32.@)
3925 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3926 UINT_PTR id
, LPCWSTR str
)
3931 if (IS_STRING_ITEM(flags
))
3932 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3934 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3936 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3937 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3938 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3939 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
3943 /*******************************************************************
3944 * ModifyMenuA (USER32.@)
3946 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3947 UINT_PTR id
, LPCSTR str
)
3951 if (IS_STRING_ITEM(flags
) && str
)
3953 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3954 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3957 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3958 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3959 HeapFree( GetProcessHeap(), 0, newstr
);
3963 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3967 /**********************************************************************
3968 * CreatePopupMenu (USER32.@)
3970 HMENU WINAPI
CreatePopupMenu(void)
3975 if (!(hmenu
= CreateMenu())) return 0;
3976 menu
= MENU_GetMenu( hmenu
);
3977 menu
->wFlags
|= MF_POPUP
;
3978 menu
->bTimeToHide
= FALSE
;
3983 /**********************************************************************
3984 * GetMenuCheckMarkDimensions (USER.417)
3985 * GetMenuCheckMarkDimensions (USER32.@)
3987 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3989 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3993 /**********************************************************************
3994 * SetMenuItemBitmaps (USER32.@)
3996 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3997 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4001 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4003 if (!hNewCheck
&& !hNewUnCheck
)
4005 item
->fState
&= ~MF_USECHECKBITMAPS
;
4007 else /* Install new bitmaps */
4009 item
->hCheckBit
= hNewCheck
;
4010 item
->hUnCheckBit
= hNewUnCheck
;
4011 item
->fState
|= MF_USECHECKBITMAPS
;
4017 /**********************************************************************
4018 * CreateMenu (USER32.@)
4020 HMENU WINAPI
CreateMenu(void)
4025 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4026 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4027 menu
->bTimeToHide
= FALSE
;
4029 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4031 TRACE("return %p\n", hMenu
);
4037 /**********************************************************************
4038 * DestroyMenu (USER32.@)
4040 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4044 TRACE("(%p)\n", hMenu
);
4046 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4047 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4049 /* DestroyMenu should not destroy system menu popup owner */
4050 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4052 DestroyWindow( lppop
->hWnd
);
4056 if (lppop
->items
) /* recursively destroy submenus */
4059 MENUITEM
*item
= lppop
->items
;
4060 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4062 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4063 MENU_FreeItemData( item
);
4065 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4067 HeapFree( GetProcessHeap(), 0, lppop
);
4072 /**********************************************************************
4073 * GetSystemMenu (USER32.@)
4075 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4077 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4080 if (wndPtr
== WND_DESKTOP
) return 0;
4081 if (wndPtr
== WND_OTHER_PROCESS
)
4083 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4087 if (wndPtr
->hSysMenu
&& bRevert
)
4089 DestroyMenu(wndPtr
->hSysMenu
);
4090 wndPtr
->hSysMenu
= 0;
4093 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4094 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4096 if( wndPtr
->hSysMenu
)
4099 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4101 /* Store the dummy sysmenu handle to facilitate the refresh */
4102 /* of the close button if the SC_CLOSE item change */
4103 menu
= MENU_GetMenu(retvalue
);
4105 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4107 WIN_ReleasePtr( wndPtr
);
4109 return bRevert
? 0 : retvalue
;
4113 /*******************************************************************
4114 * SetSystemMenu (USER32.@)
4116 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4118 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4120 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4122 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4123 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4124 WIN_ReleasePtr( wndPtr
);
4131 /**********************************************************************
4132 * GetMenu (USER32.@)
4134 HMENU WINAPI
GetMenu( HWND hWnd
)
4136 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4137 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4141 /**********************************************************************
4142 * GetMenuBarInfo (USER32.@)
4144 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4146 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4150 /**********************************************************************
4153 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4154 * SetWindowPos call that would result if SetMenu were called directly.
4156 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4158 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4160 if (hMenu
&& !IsMenu(hMenu
))
4162 WARN("hMenu %p is not a menu handle\n", hMenu
);
4165 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4168 hWnd
= WIN_GetFullHandle( hWnd
);
4169 if (GetCapture() == hWnd
)
4170 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4176 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4178 lpmenu
->hWnd
= hWnd
;
4179 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4181 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4186 /**********************************************************************
4187 * SetMenu (USER32.@)
4189 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4191 if(!MENU_SetMenu(hWnd
, hMenu
))
4194 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4195 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4200 /**********************************************************************
4201 * GetSubMenu (USER32.@)
4203 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4207 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4208 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4209 return lpmi
->hSubMenu
;
4213 /**********************************************************************
4214 * DrawMenuBar (USER32.@)
4216 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4219 HMENU hMenu
= GetMenu(hWnd
);
4221 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4223 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4225 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4226 lppop
->hwndOwner
= hWnd
;
4227 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4228 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4232 /***********************************************************************
4233 * DrawMenuBarTemp (USER32.@)
4237 * called by W98SE desk.cpl Control Panel Applet
4239 * Not 100% sure about the param names, but close.
4241 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4246 BOOL flat_menu
= FALSE
;
4248 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4251 hMenu
= GetMenu(hwnd
);
4254 hFont
= get_menu_font(FALSE
);
4256 lppop
= MENU_GetMenu( hMenu
);
4257 if (lppop
== NULL
|| lprect
== NULL
)
4259 retvalue
= GetSystemMetrics(SM_CYMENU
);
4263 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4265 hfontOld
= SelectObject( hDC
, hFont
);
4267 if (lppop
->Height
== 0)
4268 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4270 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4272 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4274 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4275 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4276 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4278 if (lppop
->nItems
== 0)
4280 retvalue
= GetSystemMetrics(SM_CYMENU
);
4284 for (i
= 0; i
< lppop
->nItems
; i
++)
4286 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4287 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4289 retvalue
= lppop
->Height
;
4292 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4296 /***********************************************************************
4297 * EndMenu (USER.187)
4298 * EndMenu (USER32.@)
4300 BOOL WINAPI
EndMenu(void)
4302 /* if we are in the menu code, and it is active */
4303 if (!fEndMenu
&& top_popup
)
4305 /* terminate the menu handling code */
4308 /* needs to be posted to wakeup the internal menu handler */
4309 /* which will now terminate the menu, in the event that */
4310 /* the main window was minimized, or lost focus, so we */
4311 /* don't end up with an orphaned menu */
4312 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4318 /*****************************************************************
4319 * LoadMenuA (USER32.@)
4321 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4323 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4324 if (!hrsrc
) return 0;
4325 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4329 /*****************************************************************
4330 * LoadMenuW (USER32.@)
4332 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4334 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4335 if (!hrsrc
) return 0;
4336 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4340 /**********************************************************************
4341 * LoadMenuIndirectW (USER32.@)
4343 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4346 WORD version
, offset
;
4347 LPCSTR p
= template;
4349 version
= GET_WORD(p
);
4351 TRACE("%p, ver %d\n", template, version
);
4354 case 0: /* standard format is version of 0 */
4355 offset
= GET_WORD(p
);
4356 p
+= sizeof(WORD
) + offset
;
4357 if (!(hMenu
= CreateMenu())) return 0;
4358 if (!MENU_ParseResource( p
, hMenu
))
4360 DestroyMenu( hMenu
);
4364 case 1: /* extended format is version of 1 */
4365 offset
= GET_WORD(p
);
4366 p
+= sizeof(WORD
) + offset
;
4367 if (!(hMenu
= CreateMenu())) return 0;
4368 if (!MENUEX_ParseResource( p
, hMenu
))
4370 DestroyMenu( hMenu
);
4375 ERR("version %d not supported.\n", version
);
4381 /**********************************************************************
4382 * LoadMenuIndirectA (USER32.@)
4384 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4386 return LoadMenuIndirectW( template );
4390 /**********************************************************************
4393 BOOL WINAPI
IsMenu(HMENU hmenu
)
4395 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4399 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4405 /**********************************************************************
4406 * GetMenuItemInfo_common
4409 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4410 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4412 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4414 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4417 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4421 if( lpmii
->fMask
& MIIM_TYPE
) {
4422 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4423 WARN("invalid combination of fMask bits used\n");
4424 /* this does not happen on Win9x/ME */
4425 SetLastError( ERROR_INVALID_PARAMETER
);
4428 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4429 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4430 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4431 if( lpmii
->fType
& MFT_BITMAP
) {
4432 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4434 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4435 /* this does not happen on Win9x/ME */
4436 lpmii
->dwTypeData
= 0;
4441 /* copy the text string */
4442 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4444 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4447 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4449 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4455 len
= strlenW(menu
->text
);
4456 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4457 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4461 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4462 0, NULL
, NULL
) - 1;
4463 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4464 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4465 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4466 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4468 /* if we've copied a substring we return its length */
4469 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4470 if (lpmii
->cch
<= len
+ 1)
4475 /* return length of string */
4476 /* not on Win9x/ME if fType & MFT_BITMAP */
4482 if (lpmii
->fMask
& MIIM_FTYPE
)
4483 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4485 if (lpmii
->fMask
& MIIM_BITMAP
)
4486 lpmii
->hbmpItem
= menu
->hbmpItem
;
4488 if (lpmii
->fMask
& MIIM_STATE
)
4489 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4491 if (lpmii
->fMask
& MIIM_ID
)
4492 lpmii
->wID
= menu
->wID
;
4494 if (lpmii
->fMask
& MIIM_SUBMENU
)
4495 lpmii
->hSubMenu
= menu
->hSubMenu
;
4497 /* hSubMenu is always cleared
4498 * (not on Win9x/ME ) */
4499 lpmii
->hSubMenu
= 0;
4502 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4503 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4504 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4506 if (lpmii
->fMask
& MIIM_DATA
)
4507 lpmii
->dwItemData
= menu
->dwItemData
;
4512 /**********************************************************************
4513 * GetMenuItemInfoA (USER32.@)
4515 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4516 LPMENUITEMINFOA lpmii
)
4520 if( lpmii
->cbSize
!= sizeof( mii
) &&
4521 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4522 SetLastError( ERROR_INVALID_PARAMETER
);
4525 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4526 mii
.cbSize
= sizeof( mii
);
4527 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4528 (LPMENUITEMINFOW
)&mii
, FALSE
);
4529 mii
.cbSize
= lpmii
->cbSize
;
4530 memcpy( lpmii
, &mii
, mii
.cbSize
);
4534 /**********************************************************************
4535 * GetMenuItemInfoW (USER32.@)
4537 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4538 LPMENUITEMINFOW lpmii
)
4542 if( lpmii
->cbSize
!= sizeof( mii
) &&
4543 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4544 SetLastError( ERROR_INVALID_PARAMETER
);
4547 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4548 mii
.cbSize
= sizeof( mii
);
4549 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4550 mii
.cbSize
= lpmii
->cbSize
;
4551 memcpy( lpmii
, &mii
, mii
.cbSize
);
4556 /* set a menu item text from a ASCII or Unicode string */
4557 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4563 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4564 strcpyW( menu
->text
, text
);
4568 LPCSTR str
= (LPCSTR
)text
;
4569 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4570 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4571 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4576 /**********************************************************************
4579 * detect if there are loops in the menu tree (or the depth is too large)
4581 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4588 if( depth
> MAXMENUDEPTH
) return depth
;
4589 item
= pmenu
->items
;
4591 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4592 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4594 int bdepth
= MENU_depth( psubmenu
, depth
);
4595 if( bdepth
> subdepth
) subdepth
= bdepth
;
4597 if( subdepth
> MAXMENUDEPTH
)
4598 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4604 /**********************************************************************
4605 * SetMenuItemInfo_common
4607 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4608 * MIIM_BITMAP and MIIM_STRING flags instead.
4611 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4612 const MENUITEMINFOW
*lpmii
,
4615 if (!menu
) return FALSE
;
4617 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4619 if (lpmii
->fMask
& MIIM_FTYPE
) {
4620 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4621 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4623 if (lpmii
->fMask
& MIIM_STRING
) {
4624 /* free the string when used */
4625 HeapFree(GetProcessHeap(), 0, menu
->text
);
4626 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4629 if (lpmii
->fMask
& MIIM_STATE
)
4630 /* Other menu items having MFS_DEFAULT are not converted
4632 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4634 if (lpmii
->fMask
& MIIM_ID
)
4635 menu
->wID
= lpmii
->wID
;
4637 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4638 menu
->hSubMenu
= lpmii
->hSubMenu
;
4639 if (menu
->hSubMenu
) {
4640 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4642 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4643 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4647 subMenu
->wFlags
|= MF_POPUP
;
4648 menu
->fType
|= MF_POPUP
;
4650 SetLastError( ERROR_INVALID_PARAMETER
);
4655 menu
->fType
&= ~MF_POPUP
;
4658 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4660 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4661 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4663 if (lpmii
->fMask
& MIIM_DATA
)
4664 menu
->dwItemData
= lpmii
->dwItemData
;
4666 if (lpmii
->fMask
& MIIM_BITMAP
)
4667 menu
->hbmpItem
= lpmii
->hbmpItem
;
4669 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4670 menu
->fType
|= MFT_SEPARATOR
;
4672 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4676 /**********************************************************************
4677 * MENU_NormalizeMenuItemInfoStruct
4679 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4680 * check, copy and extend the MENUITEMINFO struct from the version that the application
4681 * supplied to the version used by wine source. */
4682 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4683 MENUITEMINFOW
*pmii_out
)
4685 /* do we recognize the size? */
4686 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4687 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) {
4688 SetLastError( ERROR_INVALID_PARAMETER
);
4691 /* copy the fields that we have */
4692 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4693 /* if the hbmpItem member is missing then extend */
4694 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4695 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4696 pmii_out
->hbmpItem
= NULL
;
4698 /* test for invalid bit combinations */
4699 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4700 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4701 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4702 WARN("invalid combination of fMask bits used\n");
4703 /* this does not happen on Win9x/ME */
4704 SetLastError( ERROR_INVALID_PARAMETER
);
4707 /* convert old style (MIIM_TYPE) to the new */
4708 if( pmii_out
->fMask
& MIIM_TYPE
){
4709 pmii_out
->fMask
|= MIIM_FTYPE
;
4710 if( IS_STRING_ITEM(pmii_out
->fType
)){
4711 pmii_out
->fMask
|= MIIM_STRING
;
4712 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4713 pmii_out
->fMask
|= MIIM_BITMAP
;
4714 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4720 /**********************************************************************
4721 * SetMenuItemInfoA (USER32.@)
4723 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4724 const MENUITEMINFOA
*lpmii
)
4728 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4730 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4732 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4736 /**********************************************************************
4737 * SetMenuItemInfoW (USER32.@)
4739 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4740 const MENUITEMINFOW
*lpmii
)
4744 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4746 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4747 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4748 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4751 /**********************************************************************
4752 * SetMenuDefaultItem (USER32.@)
4755 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4761 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4763 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4765 /* reset all default-item flags */
4767 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4769 item
->fState
&= ~MFS_DEFAULT
;
4772 /* no default item */
4781 if ( uItem
>= menu
->nItems
) return FALSE
;
4782 item
[uItem
].fState
|= MFS_DEFAULT
;
4787 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4789 if (item
->wID
== uItem
)
4791 item
->fState
|= MFS_DEFAULT
;
4800 /**********************************************************************
4801 * GetMenuDefaultItem (USER32.@)
4803 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4809 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4811 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4813 /* find default item */
4817 if (! item
) return -1;
4819 while ( !( item
->fState
& MFS_DEFAULT
) )
4822 if (i
>= menu
->nItems
) return -1;
4825 /* default: don't return disabled items */
4826 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4828 /* search rekursiv when needed */
4829 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4832 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4833 if ( -1 != ret
) return ret
;
4835 /* when item not found in submenu, return the popup item */
4837 return ( bypos
) ? i
: item
->wID
;
4842 /**********************************************************************
4843 * InsertMenuItemA (USER32.@)
4845 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4846 const MENUITEMINFOA
*lpmii
)
4851 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4853 if (!MENU_NormalizeMenuItemInfoStruct( (MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4855 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4856 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
4860 /**********************************************************************
4861 * InsertMenuItemW (USER32.@)
4863 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4864 const MENUITEMINFOW
*lpmii
)
4869 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4871 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4873 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4874 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4877 /**********************************************************************
4878 * CheckMenuRadioItem (USER32.@)
4881 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4882 UINT first
, UINT last
, UINT check
,
4887 MENUITEM
*mi_first
= NULL
, *mi_check
;
4888 HMENU m_first
, m_check
;
4890 for (i
= first
; i
<= last
; i
++)
4897 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4898 if (!mi_first
) continue;
4899 mi_check
= mi_first
;
4905 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4906 if (!mi_check
) continue;
4909 if (m_first
!= m_check
) continue;
4910 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4914 mi_check
->fType
|= MFT_RADIOCHECK
;
4915 mi_check
->fState
|= MFS_CHECKED
;
4920 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4921 mi_check
->fState
&= ~MFS_CHECKED
;
4929 /**********************************************************************
4930 * GetMenuItemRect (USER32.@)
4932 * ATTENTION: Here, the returned values in rect are the screen
4933 * coordinates of the item just like if the menu was
4934 * always on the upper left side of the application.
4937 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4940 POPUPMENU
*itemMenu
;
4944 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4946 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4947 referenceHwnd
= hwnd
;
4951 itemMenu
= MENU_GetMenu(hMenu
);
4952 if (itemMenu
== NULL
)
4955 if(itemMenu
->hWnd
== 0)
4957 referenceHwnd
= itemMenu
->hWnd
;
4960 if ((rect
== NULL
) || (item
== NULL
))
4965 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4970 /**********************************************************************
4971 * SetMenuInfo (USER32.@)
4974 * actually use the items to draw the menu
4975 * (recalculate and/or redraw)
4977 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
4980 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
4982 if (lpmi
->fMask
& MIM_BACKGROUND
)
4983 menu
->hbrBack
= lpmi
->hbrBack
;
4985 if (lpmi
->fMask
& MIM_HELPID
)
4986 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4988 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4989 menu
->cyMax
= lpmi
->cyMax
;
4991 if (lpmi
->fMask
& MIM_MENUDATA
)
4992 menu
->dwMenuData
= lpmi
->dwMenuData
;
4994 if (lpmi
->fMask
& MIM_STYLE
)
4995 menu
->dwStyle
= lpmi
->dwStyle
;
4997 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
4999 MENUITEM
*item
= menu
->items
;
5000 for( i
= menu
->nItems
; i
; i
--, item
++)
5001 if( item
->fType
& MF_POPUP
)
5002 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5007 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5009 TRACE("(%p %p)\n", hMenu
, lpmi
);
5010 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5011 if( lpmi
->fMask
& MIM_STYLE
) {
5012 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5013 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5014 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5018 SetLastError( ERROR_INVALID_PARAMETER
);
5022 /**********************************************************************
5023 * GetMenuInfo (USER32.@)
5029 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5032 TRACE("(%p %p)\n", hMenu
, lpmi
);
5034 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5037 if (lpmi
->fMask
& MIM_BACKGROUND
)
5038 lpmi
->hbrBack
= menu
->hbrBack
;
5040 if (lpmi
->fMask
& MIM_HELPID
)
5041 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5043 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5044 lpmi
->cyMax
= menu
->cyMax
;
5046 if (lpmi
->fMask
& MIM_MENUDATA
)
5047 lpmi
->dwMenuData
= menu
->dwMenuData
;
5049 if (lpmi
->fMask
& MIM_STYLE
)
5050 lpmi
->dwStyle
= menu
->dwStyle
;
5054 SetLastError( ERROR_INVALID_PARAMETER
);
5059 /**********************************************************************
5060 * SetMenuContextHelpId (USER32.@)
5062 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5066 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5068 if ((menu
= MENU_GetMenu(hMenu
)))
5070 menu
->dwContextHelpID
= dwContextHelpID
;
5077 /**********************************************************************
5078 * GetMenuContextHelpId (USER32.@)
5080 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5084 TRACE("(%p)\n", hMenu
);
5086 if ((menu
= MENU_GetMenu(hMenu
)))
5088 return menu
->dwContextHelpID
;
5093 /**********************************************************************
5094 * MenuItemFromPoint (USER32.@)
5096 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5098 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5101 /*FIXME: Do we have to handle hWnd here? */
5102 if (!menu
) return -1;
5103 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5108 /**********************************************************************
5109 * translate_accelerator
5111 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5112 BYTE fVirt
, WORD key
, WORD cmd
)
5117 if (wParam
!= key
) return FALSE
;
5119 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5120 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5121 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5123 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5125 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5127 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5133 if(fVirt
& FVIRTKEY
)
5135 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5136 wParam
, 0xff & HIWORD(lParam
));
5138 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5139 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5143 if (!(lParam
& 0x01000000)) /* no special_key */
5145 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5146 { /* ^^ ALT pressed */
5147 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5156 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5160 HMENU hMenu
, hSubMenu
, hSysMenu
;
5161 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5163 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5164 hSysMenu
= get_win_sys_menu( hWnd
);
5166 /* find menu item and ask application to initialize it */
5167 /* 1. in the system menu */
5168 hSubMenu
= hSysMenu
;
5170 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5174 if (!IsWindowEnabled(hWnd
))
5178 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5179 if(hSubMenu
!= hSysMenu
)
5181 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5182 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5183 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5185 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5188 else /* 2. in the window's menu */
5192 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5196 if (!IsWindowEnabled(hWnd
))
5200 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5201 if(hSubMenu
!= hMenu
)
5203 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5204 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5205 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5207 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5214 if (uSysStat
!= (UINT
)-1)
5216 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5223 if (uStat
!= (UINT
)-1)
5229 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5241 if( mesg
==WM_COMMAND
)
5243 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5244 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5246 else if( mesg
==WM_SYSCOMMAND
)
5248 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5249 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5253 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5254 * #0: unknown (please report!)
5255 * #1: for WM_KEYUP,WM_SYSKEYUP
5256 * #2: mouse is captured
5257 * #3: window is disabled
5258 * #4: it's a disabled system menu option
5259 * #5: it's a menu option, but window is iconic
5260 * #6: it's a menu option, but disabled
5262 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5264 ERR_(accel
)(" unknown reason - please report!\n");
5269 /**********************************************************************
5270 * TranslateAcceleratorA (USER32.@)
5271 * TranslateAccelerator (USER32.@)
5273 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5275 switch (msg
->message
)
5279 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5285 char ch
= LOWORD(msg
->wParam
);
5287 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5288 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5289 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5297 /**********************************************************************
5298 * TranslateAcceleratorW (USER32.@)
5300 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5302 ACCEL data
[32], *ptr
= data
;
5305 if (!hWnd
) return 0;
5307 if (msg
->message
!= WM_KEYDOWN
&&
5308 msg
->message
!= WM_SYSKEYDOWN
&&
5309 msg
->message
!= WM_CHAR
&&
5310 msg
->message
!= WM_SYSCHAR
)
5313 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5314 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5316 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5317 if (count
> sizeof(data
)/sizeof(data
[0]))
5319 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5321 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5322 for (i
= 0; i
< count
; i
++)
5324 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5325 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5328 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);