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
;
730 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
731 else pt
.x
-= rect
.left
;
734 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
737 MENU_AdjustMenuItemRect(menu
, &rect
);
738 if (PtInRect(&rect
, pt
))
748 /***********************************************************************
751 * Find the menu item selected by a key press.
752 * Return item id, -1 if none, -2 if we should close the menu.
754 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
755 WCHAR key
, BOOL forceMenuChar
)
757 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
759 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
763 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
764 MENUITEM
*item
= menu
->items
;
771 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
775 WCHAR
*p
= item
->text
- 2;
778 p
= strchrW (p
+ 2, '&');
780 while (p
!= NULL
&& p
[1] == '&');
781 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
785 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
786 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
787 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
788 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
794 /***********************************************************************
795 * MENU_GetBitmapItemSize
797 * Get the size of a bitmap item.
799 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
803 HBITMAP bmp
= lpitem
->hbmpItem
;
805 size
->cx
= size
->cy
= 0;
807 /* check if there is a magic menu item associated with this item */
808 switch( (INT_PTR
) bmp
)
810 case (INT_PTR
)HBMMENU_CALLBACK
:
812 MEASUREITEMSTRUCT measItem
;
813 measItem
.CtlType
= ODT_MENU
;
815 measItem
.itemID
= lpitem
->wID
;
816 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
817 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
818 measItem
.itemData
= lpitem
->dwItemData
;
819 SendMessageW( hwndOwner
, WM_MEASUREITEM
, lpitem
->wID
, (LPARAM
)&measItem
);
820 size
->cx
= measItem
.itemWidth
;
821 size
->cy
= measItem
.itemHeight
;
825 case (INT_PTR
)HBMMENU_SYSTEM
:
826 if (lpitem
->dwItemData
)
828 bmp
= (HBITMAP
)lpitem
->dwItemData
;
832 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
833 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
834 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
835 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
836 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
837 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
840 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
841 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
842 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
843 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
844 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
845 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
848 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
850 size
->cx
= bm
.bmWidth
;
851 size
->cy
= bm
.bmHeight
;
855 /***********************************************************************
856 * MENU_DrawBitmapItem
858 * Draw a bitmap item.
860 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
861 HMENU hmenu
, HWND hwndOwner
, UINT odaction
, BOOL menuBar
)
867 int w
= rect
->right
- rect
->left
;
868 int h
= rect
->bottom
- rect
->top
;
871 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
874 /* Check if there is a magic menu item associated with this item */
875 if (IS_MAGIC_BITMAP(hbmToDraw
))
881 switch((INT_PTR
)hbmToDraw
)
883 case (INT_PTR
)HBMMENU_SYSTEM
:
884 if (lpitem
->dwItemData
)
886 bmp
= (HBITMAP
)lpitem
->dwItemData
;
887 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
891 static HBITMAP hBmpSysMenu
;
893 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
895 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
896 /* only use right half of the bitmap */
897 bmp_xoffset
= bm
.bmWidth
/ 2;
898 bm
.bmWidth
-= bmp_xoffset
;
901 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
902 flags
= DFCS_CAPTIONRESTORE
;
904 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
905 flags
= DFCS_CAPTIONMIN
;
907 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
908 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
910 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
911 flags
= DFCS_CAPTIONCLOSE
;
913 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
914 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
916 case (INT_PTR
)HBMMENU_CALLBACK
:
918 DRAWITEMSTRUCT drawItem
;
919 drawItem
.CtlType
= ODT_MENU
;
921 drawItem
.itemID
= lpitem
->wID
;
922 drawItem
.itemAction
= odaction
;
923 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
924 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
925 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
926 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
927 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
928 drawItem
.hwndItem
= (HWND
)hmenu
;
930 drawItem
.itemData
= lpitem
->dwItemData
;
931 drawItem
.rcItem
= *rect
;
932 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
936 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
939 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
942 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
945 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
949 FIXME("Magic %p not implemented\n", hbmToDraw
);
954 /* draw the magic bitmaps using marlett font characters */
955 /* FIXME: fontsize and the position (x,y) could probably be better */
956 HFONT hfont
, hfontsav
;
957 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
958 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
959 { 'M','a','r','l','e','t','t',0 } };
960 logfont
.lfHeight
= min( h
, w
) - 5 ;
961 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
962 hfont
= CreateFontIndirectW( &logfont
);
963 hfontsav
= SelectObject(hdc
, hfont
);
964 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
965 SelectObject(hdc
, hfontsav
);
966 DeleteObject( hfont
);
971 InflateRect( &r
, -1, -1 );
972 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
973 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
978 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
981 hdcMem
= CreateCompatibleDC( hdc
);
982 SelectObject( hdcMem
, bmp
);
984 /* handle fontsize > bitmap_height */
985 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
987 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
988 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
989 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
990 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
995 /***********************************************************************
998 * Calculate the size of the menu item and store it in lpitem->rect.
1000 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1001 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1004 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1005 UINT arrow_bitmap_width
;
1009 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1010 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1011 (menuBar
? " (MenuBar)" : ""));
1013 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1014 arrow_bitmap_width
= bm
.bmWidth
;
1016 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1017 if( !menucharsize
.cx
) {
1018 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1019 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1020 * but it is unlikely an application will depend on that */
1021 ODitemheight
= HIWORD( GetDialogBaseUnits());
1024 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1026 if (lpitem
->fType
& MF_OWNERDRAW
)
1028 MEASUREITEMSTRUCT mis
;
1029 mis
.CtlType
= ODT_MENU
;
1031 mis
.itemID
= lpitem
->wID
;
1032 mis
.itemData
= lpitem
->dwItemData
;
1033 mis
.itemHeight
= ODitemheight
;
1035 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1036 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1037 * width of a menufont character to the width of an owner-drawn menu.
1039 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1041 /* under at least win95 you seem to be given a standard
1042 height for the menu and the height value is ignored */
1043 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1045 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1047 TRACE("id=%04lx size=%dx%d\n",
1048 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1049 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1053 if (lpitem
->fType
& MF_SEPARATOR
)
1055 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1057 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1065 if (lpitem
->hbmpItem
) {
1068 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1069 /* Keep the size of the bitmap in callback mode to be able
1070 * to draw it correctly */
1071 lpitem
->bmpsize
= size
;
1072 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1073 lpitem
->rect
.right
+= size
.cx
+ 2;
1074 itemheight
= size
.cy
+ 2;
1076 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1077 lpitem
->rect
.right
+= check_bitmap_width
;
1078 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1079 lpitem
->xTab
= lpitem
->rect
.right
;
1080 lpitem
->rect
.right
+= arrow_bitmap_width
;
1081 } else if (lpitem
->hbmpItem
) { /* menuBar */
1084 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1085 lpitem
->bmpsize
= size
;
1086 lpitem
->rect
.right
+= size
.cx
;
1087 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1088 itemheight
= size
.cy
;
1091 /* it must be a text item - unless it's the system menu */
1092 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1093 HFONT hfontOld
= NULL
;
1094 RECT rc
= lpitem
->rect
;
1095 LONG txtheight
, txtwidth
;
1097 if ( lpitem
->fState
& MFS_DEFAULT
) {
1098 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1101 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1102 DT_SINGLELINE
|DT_CALCRECT
);
1103 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1104 itemheight
= max( max( itemheight
, txtheight
),
1105 GetSystemMetrics( SM_CYMENU
) - 1);
1106 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1108 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1111 int n
= (int)( p
- lpitem
->text
);
1112 /* Item contains a tab (only meaningful in popup menus) */
1113 /* get text size before the tab */
1114 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1115 DT_SINGLELINE
|DT_CALCRECT
);
1116 txtwidth
= rc
.right
- rc
.left
;
1117 p
+= 1; /* advance past the Tab */
1118 /* get text size after the tab */
1119 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1120 DT_SINGLELINE
|DT_CALCRECT
);
1121 lpitem
->xTab
+= txtwidth
;
1122 txtheight
= max( txtheight
, tmpheight
);
1123 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1124 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1126 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1127 DT_SINGLELINE
|DT_CALCRECT
);
1128 txtwidth
= rc
.right
- rc
.left
;
1129 lpitem
->xTab
+= txtwidth
;
1131 lpitem
->rect
.right
+= 2 + txtwidth
;
1132 itemheight
= max( itemheight
,
1133 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1135 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1136 } else if( menuBar
) {
1137 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1139 lpitem
->rect
.bottom
+= itemheight
;
1140 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1144 /***********************************************************************
1145 * MENU_GetMaxPopupHeight
1148 MENU_GetMaxPopupHeight(const POPUPMENU
*lppop
)
1151 return lppop
->cyMax
;
1152 return GetSystemMetrics(SM_CYSCREEN
) - GetSystemMetrics(SM_CYBORDER
);
1156 /***********************************************************************
1157 * MENU_PopupMenuCalcSize
1159 * Calculate the size of a popup menu.
1161 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
)
1166 int textandbmp
= FALSE
;
1167 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
, maxHeight
;
1169 lppop
->Width
= lppop
->Height
= 0;
1170 if (lppop
->nItems
== 0) return;
1173 SelectObject( hdc
, get_menu_font(FALSE
));
1178 lppop
->textOffset
= 0;
1180 while (start
< lppop
->nItems
)
1182 lpitem
= &lppop
->items
[start
];
1184 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1185 orgX
+= MENU_COL_SPACE
;
1186 orgY
= MENU_TOP_MARGIN
;
1188 maxTab
= maxTabWidth
= 0;
1189 /* Parse items until column break or end of menu */
1190 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1193 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1195 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1196 maxX
= max( maxX
, lpitem
->rect
.right
);
1197 orgY
= lpitem
->rect
.bottom
;
1198 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1200 maxTab
= max( maxTab
, lpitem
->xTab
);
1201 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1203 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1206 /* Finish the column (set all items to the largest width found) */
1207 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1208 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1210 lpitem
->rect
.right
= maxX
;
1211 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1212 lpitem
->xTab
= maxTab
;
1215 lppop
->Height
= max( lppop
->Height
, orgY
);
1218 lppop
->Width
= maxX
;
1219 /* if none of the items have both text and bitmap then
1220 * the text and bitmaps are all aligned on the left. If there is at
1221 * least one item with both text and bitmap then bitmaps are
1222 * on the left and texts left aligned with the right hand side
1224 if( !textandbmp
) lppop
->textOffset
= 0;
1226 /* space for 3d border */
1227 lppop
->Height
+= MENU_BOTTOM_MARGIN
;
1230 /* Adjust popup height if it exceeds maximum */
1231 maxHeight
= MENU_GetMaxPopupHeight(lppop
);
1232 lppop
->nTotalHeight
= lppop
->Height
- MENU_TOP_MARGIN
;
1233 if (lppop
->Height
>= maxHeight
)
1235 lppop
->Height
= maxHeight
;
1236 lppop
->bScrolling
= TRUE
;
1240 lppop
->bScrolling
= FALSE
;
1243 ReleaseDC( 0, hdc
);
1247 /***********************************************************************
1248 * MENU_MenuBarCalcSize
1250 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1251 * height is off by 1 pixel which causes lengthy window relocations when
1252 * active document window is maximized/restored.
1254 * Calculate the size of the menu bar.
1256 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1257 LPPOPUPMENU lppop
, HWND hwndOwner
)
1260 UINT start
, i
, helpPos
;
1261 int orgX
, orgY
, maxY
;
1263 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1264 if (lppop
->nItems
== 0) return;
1265 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1266 lppop
->Width
= lprect
->right
- lprect
->left
;
1268 maxY
= lprect
->top
+1;
1271 lppop
->textOffset
= 0;
1272 while (start
< lppop
->nItems
)
1274 lpitem
= &lppop
->items
[start
];
1275 orgX
= lprect
->left
;
1278 /* Parse items until line break or end of menu */
1279 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1281 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1283 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1285 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1286 debug_print_menuitem (" item: ", lpitem
, "");
1287 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1289 if (lpitem
->rect
.right
> lprect
->right
)
1291 if (i
!= start
) break;
1292 else lpitem
->rect
.right
= lprect
->right
;
1294 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1295 orgX
= lpitem
->rect
.right
;
1298 /* Finish the line (set all items to the largest height found) */
1299 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1302 lprect
->bottom
= maxY
;
1303 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1305 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1306 /* the last item (if several lines, only move the last line) */
1307 if (helpPos
== ~0U) return;
1308 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1309 orgY
= lpitem
->rect
.top
;
1310 orgX
= lprect
->right
;
1311 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1312 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1313 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1314 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1315 lpitem
->rect
.right
= orgX
;
1316 orgX
= lpitem
->rect
.left
;
1321 /***********************************************************************
1322 * MENU_DrawScrollArrows
1324 * Draw scroll arrows.
1327 MENU_DrawScrollArrows(const POPUPMENU
*lppop
, HDC hdc
)
1329 HDC hdcMem
= CreateCompatibleDC(hdc
);
1330 HBITMAP hOrigBitmap
;
1331 UINT arrow_bitmap_width
, arrow_bitmap_height
;
1335 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1336 arrow_bitmap_width
= bmp
.bmWidth
;
1337 arrow_bitmap_height
= bmp
.bmHeight
;
1340 if (lppop
->nScrollPos
)
1341 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_bitmap());
1343 hOrigBitmap
= SelectObject(hdcMem
, get_up_arrow_inactive_bitmap());
1346 rect
.right
= lppop
->Width
;
1347 rect
.bottom
= arrow_bitmap_height
;
1348 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1349 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2, 0,
1350 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1351 rect
.top
= lppop
->Height
- arrow_bitmap_height
;
1352 rect
.bottom
= lppop
->Height
;
1353 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENU
));
1354 if (lppop
->nScrollPos
< lppop
->nTotalHeight
- (MENU_GetMaxPopupHeight(lppop
) - 2 * arrow_bitmap_height
))
1355 SelectObject(hdcMem
, get_down_arrow_bitmap());
1357 SelectObject(hdcMem
, get_down_arrow_inactive_bitmap());
1358 BitBlt(hdc
, (lppop
->Width
- arrow_bitmap_width
) / 2,
1359 lppop
->Height
- arrow_bitmap_height
,
1360 arrow_bitmap_width
, arrow_bitmap_height
, hdcMem
, 0, 0, SRCCOPY
);
1361 SelectObject(hdcMem
, hOrigBitmap
);
1366 /***********************************************************************
1369 * Draws the popup-menu arrow.
1371 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1372 UINT arrow_bitmap_height
)
1374 HDC hdcMem
= CreateCompatibleDC( hdc
);
1375 HBITMAP hOrigBitmap
;
1377 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1378 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1379 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1380 arrow_bitmap_width
, arrow_bitmap_height
,
1381 hdcMem
, 0, 0, SRCCOPY
);
1382 SelectObject( hdcMem
, hOrigBitmap
);
1385 /***********************************************************************
1388 * Draw a single menu item.
1390 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1391 UINT height
, BOOL menuBar
, UINT odaction
)
1394 BOOL flat_menu
= FALSE
;
1396 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1397 POPUPMENU
*menu
= MENU_GetMenu(hmenu
);
1400 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1404 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1405 arrow_bitmap_width
= bmp
.bmWidth
;
1406 arrow_bitmap_height
= bmp
.bmHeight
;
1409 if (lpitem
->fType
& MF_SYSMENU
)
1411 if( !IsIconic(hwnd
) )
1412 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1416 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1417 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1421 if (lpitem
->fState
& MF_HILITE
)
1423 if(menuBar
&& !flat_menu
) {
1424 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1425 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1427 if(lpitem
->fState
& MF_GRAYED
)
1428 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1430 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1431 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1436 if (lpitem
->fState
& MF_GRAYED
)
1437 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1439 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1440 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1443 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1444 rect
= lpitem
->rect
;
1445 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu
), &rect
);
1447 if (lpitem
->fType
& MF_OWNERDRAW
)
1450 ** Experimentation under Windows reveals that an owner-drawn
1451 ** menu is given the rectangle which includes the space it requested
1452 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1453 ** and a popup-menu arrow. This is the value of lpitem->rect.
1454 ** Windows will leave all drawing to the application except for
1455 ** the popup-menu arrow. Windows always draws that itself, after
1456 ** the menu owner has finished drawing.
1460 dis
.CtlType
= ODT_MENU
;
1462 dis
.itemID
= lpitem
->wID
;
1463 dis
.itemData
= lpitem
->dwItemData
;
1465 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1466 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1467 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1468 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1469 dis
.hwndItem
= (HWND
)hmenu
;
1472 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1473 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1474 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1475 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1476 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1477 /* Draw the popup-menu arrow */
1478 if (lpitem
->fType
& MF_POPUP
)
1479 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1480 arrow_bitmap_height
);
1484 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1486 if (lpitem
->fState
& MF_HILITE
)
1490 InflateRect (&rect
, -1, -1);
1491 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1492 InflateRect (&rect
, 1, 1);
1493 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1498 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1500 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1504 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1506 SetBkMode( hdc
, TRANSPARENT
);
1508 /* vertical separator */
1509 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1514 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1516 rc
.bottom
= height
- 3;
1519 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1520 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1521 LineTo( hdc
, rc
.left
, rc
.bottom
);
1522 SelectObject( hdc
, oldPen
);
1525 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1528 /* horizontal separator */
1529 if (lpitem
->fType
& MF_SEPARATOR
)
1536 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1539 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1540 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1541 LineTo( hdc
, rc
.right
, rc
.top
);
1542 SelectObject( hdc
, oldPen
);
1545 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1549 /* helper lines for debugging */
1550 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1551 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1552 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1553 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1556 if (lpitem
->hbmpItem
) {
1557 /* calculate the bitmap rectangle in coordinates relative
1558 * to the item rectangle */
1560 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1563 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1565 else if (menu
->dwStyle
& MNS_NOCHECK
)
1567 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1570 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1571 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1572 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1575 bmprc
.top
= (rect
.bottom
- rect
.top
-
1576 lpitem
->bmpsize
.cy
) / 2;
1577 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1583 INT y
= rect
.top
+ rect
.bottom
;
1585 int checked
= FALSE
;
1586 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1587 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1588 /* Draw the check mark
1591 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1593 if( !(menu
->dwStyle
& MNS_NOCHECK
)) {
1594 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1595 lpitem
->hUnCheckBit
;
1596 if (bm
) /* we have a custom bitmap */
1598 HDC hdcMem
= CreateCompatibleDC( hdc
);
1600 SelectObject( hdcMem
, bm
);
1601 BitBlt( hdc
, rc
.left
, (y
- check_bitmap_height
) / 2,
1602 check_bitmap_width
, check_bitmap_height
,
1603 hdcMem
, 0, 0, SRCCOPY
);
1607 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1610 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1611 check_bitmap_height
, 1, 1, NULL
);
1612 HDC hdcMem
= CreateCompatibleDC( hdc
);
1614 SelectObject( hdcMem
, bm
);
1615 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1616 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1617 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1618 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1619 BitBlt( hdc
, rc
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1620 hdcMem
, 0, 0, SRCCOPY
);
1626 if( lpitem
->hbmpItem
&&
1627 !( checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
))) {
1629 /* some applications make this assumption on the DC's origin */
1630 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1631 MENU_DrawBitmapItem(hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1633 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1635 /* Draw the popup-menu arrow */
1636 if (lpitem
->fType
& MF_POPUP
)
1637 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1638 arrow_bitmap_height
);
1640 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1641 rect
.left
+= check_bitmap_width
;
1642 rect
.right
-= arrow_bitmap_width
;
1644 else if( lpitem
->hbmpItem
)
1645 { /* Draw the bitmap */
1648 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1649 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, hmenu
, hwndOwner
,
1651 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1653 /* process text if present */
1659 UINT uFormat
= (menuBar
) ?
1660 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1661 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1663 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1664 rect
.left
+= menu
->textOffset
;
1666 if ( lpitem
->fState
& MFS_DEFAULT
)
1668 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1672 if( lpitem
->hbmpItem
)
1673 rect
.left
+= lpitem
->bmpsize
.cx
;
1674 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1675 rect
.left
+= menucharsize
.cx
;
1676 rect
.right
-= menucharsize
.cx
;
1679 for (i
= 0; lpitem
->text
[i
]; i
++)
1680 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1683 if(lpitem
->fState
& MF_GRAYED
)
1685 if (!(lpitem
->fState
& MF_HILITE
) )
1687 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1688 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1689 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1690 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1692 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1695 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1697 /* paint the shortcut text */
1698 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1700 if (lpitem
->text
[i
] == '\t')
1702 rect
.left
= lpitem
->xTab
;
1703 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1707 rect
.right
= lpitem
->xTab
;
1708 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1711 if(lpitem
->fState
& MF_GRAYED
)
1713 if (!(lpitem
->fState
& MF_HILITE
) )
1715 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1716 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1717 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1718 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1720 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1722 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1726 SelectObject (hdc
, hfontOld
);
1731 /***********************************************************************
1732 * MENU_DrawPopupMenu
1734 * Paint a popup menu.
1736 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1738 HBRUSH hPrevBrush
= 0;
1741 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1743 GetClientRect( hwnd
, &rect
);
1745 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1746 && (SelectObject( hdc
, get_menu_font(FALSE
))))
1750 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1752 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1756 BOOL flat_menu
= FALSE
;
1758 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1760 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1762 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1764 if( (menu
= MENU_GetMenu( hmenu
)))
1766 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1767 /* draw menu items */
1774 for( u
= menu
->nItems
; u
> 0; u
--, item
++)
1775 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
,
1776 item
, menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1778 /* draw scroll arrows */
1779 if (menu
->bScrolling
)
1780 MENU_DrawScrollArrows(menu
, hdc
);
1784 SelectObject( hdc
, hPrevBrush
);
1789 /***********************************************************************
1792 * Paint a menu bar. Returns the height of the menu bar.
1793 * called from [windows/nonclient.c]
1795 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1800 HMENU hMenu
= GetMenu(hwnd
);
1802 lppop
= MENU_GetMenu( hMenu
);
1803 if (lppop
== NULL
|| lprect
== NULL
)
1805 return GetSystemMetrics(SM_CYMENU
);
1810 hfontOld
= SelectObject( hDC
, get_menu_font(FALSE
));
1812 if (lppop
->Height
== 0)
1813 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1815 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1817 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1818 return lppop
->Height
;
1821 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1825 /***********************************************************************
1828 * Display a popup menu.
1830 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1831 INT x
, INT y
, INT xanchor
, INT yanchor
)
1840 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1841 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1843 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1844 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1846 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1847 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1850 /* store the owner for DrawItem */
1851 menu
->hwndOwner
= hwndOwner
;
1853 menu
->nScrollPos
= 0;
1854 MENU_PopupMenuCalcSize( menu
);
1856 /* adjust popup menu pos so that it fits within the desktop */
1858 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1859 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1861 /* FIXME: should use item rect */
1864 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1865 info
.cbSize
= sizeof(info
);
1866 GetMonitorInfoW( monitor
, &info
);
1868 if (flags
& TPM_LAYOUTRTL
)
1870 ex_style
= WS_EX_LAYOUTRTL
;
1871 flags
^= TPM_RIGHTALIGN
;
1874 if( flags
& TPM_RIGHTALIGN
) x
-= width
;
1875 if( flags
& TPM_CENTERALIGN
) x
-= width
/ 2;
1877 if( flags
& TPM_BOTTOMALIGN
) y
-= height
;
1878 if( flags
& TPM_VCENTERALIGN
) y
-= height
/ 2;
1880 if( x
+ width
> info
.rcWork
.right
)
1882 if( xanchor
&& x
>= width
- xanchor
)
1883 x
-= width
- xanchor
;
1885 if( x
+ width
> info
.rcWork
.right
)
1886 x
= info
.rcWork
.right
- width
;
1888 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1890 if( y
+ height
> info
.rcWork
.bottom
)
1892 if( yanchor
&& y
>= height
+ yanchor
)
1893 y
-= height
+ yanchor
;
1895 if( y
+ height
> info
.rcWork
.bottom
)
1896 y
= info
.rcWork
.bottom
- height
;
1898 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1900 /* NOTE: In Windows, top menu popup is not owned. */
1901 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1902 WS_POPUP
, x
, y
, width
, height
,
1903 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1905 if( !menu
->hWnd
) return FALSE
;
1907 top_popup
= menu
->hWnd
;
1908 top_popup_hmenu
= hmenu
;
1910 /* Display the window */
1912 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1913 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1914 UpdateWindow( menu
->hWnd
);
1919 /***********************************************************************
1920 * MENU_EnsureMenuItemVisible
1923 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1925 if (lppop
->bScrolling
)
1927 MENUITEM
*item
= &lppop
->items
[wIndex
];
1928 UINT nMaxHeight
= MENU_GetMaxPopupHeight(lppop
);
1929 UINT nOldPos
= lppop
->nScrollPos
;
1931 UINT arrow_bitmap_height
;
1934 GetClientRect(lppop
->hWnd
, &rc
);
1936 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp
), &bmp
);
1937 arrow_bitmap_height
= bmp
.bmHeight
;
1939 rc
.top
+= arrow_bitmap_height
;
1940 rc
.bottom
-= arrow_bitmap_height
+ MENU_BOTTOM_MARGIN
;
1942 nMaxHeight
-= GetSystemMetrics(SM_CYBORDER
) + 2 * arrow_bitmap_height
;
1943 if (item
->rect
.bottom
> lppop
->nScrollPos
+ nMaxHeight
)
1946 lppop
->nScrollPos
= item
->rect
.bottom
- nMaxHeight
;
1947 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1948 MENU_DrawScrollArrows(lppop
, hdc
);
1950 else if (item
->rect
.top
- MENU_TOP_MARGIN
< lppop
->nScrollPos
)
1952 lppop
->nScrollPos
= item
->rect
.top
- MENU_TOP_MARGIN
;
1953 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, &rc
, &rc
);
1954 MENU_DrawScrollArrows(lppop
, hdc
);
1960 /***********************************************************************
1963 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1964 BOOL sendMenuSelect
, HMENU topmenu
)
1969 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1971 lppop
= MENU_GetMenu( hmenu
);
1972 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1974 if (lppop
->FocusedItem
== wIndex
) return;
1975 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1976 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1978 top_popup
= lppop
->hWnd
;
1979 top_popup_hmenu
= hmenu
;
1982 SelectObject( hdc
, get_menu_font(FALSE
));
1984 /* Clear previous highlighted item */
1985 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1987 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1988 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1989 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1993 /* Highlight new item (if any) */
1994 lppop
->FocusedItem
= wIndex
;
1995 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1997 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1998 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1999 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
2000 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
2001 &lppop
->items
[wIndex
], lppop
->Height
,
2002 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2006 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2007 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2008 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2009 ip
->fType
| ip
->fState
|
2010 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2013 else if (sendMenuSelect
) {
2016 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2017 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2018 MENUITEM
*ip
= &ptm
->items
[pos
];
2019 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2020 ip
->fType
| ip
->fState
|
2021 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2025 ReleaseDC( lppop
->hWnd
, hdc
);
2029 /***********************************************************************
2030 * MENU_MoveSelection
2032 * Moves currently selected item according to the offset parameter.
2033 * If there is no selection then it should select the last item if
2034 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2036 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2041 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2043 menu
= MENU_GetMenu( hmenu
);
2044 if ((!menu
) || (!menu
->items
)) return;
2046 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2048 if( menu
->nItems
== 1 ) return; else
2049 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2051 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2053 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2058 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2059 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2060 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2062 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2068 /**********************************************************************
2071 * Insert (allocate) a new item into a menu.
2073 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
2078 if (!(menu
= MENU_GetMenu(hMenu
)))
2081 /* Find where to insert new item */
2083 if (flags
& MF_BYPOSITION
) {
2084 if (pos
> menu
->nItems
)
2087 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
2090 if (!(menu
= MENU_GetMenu( hMenu
)))
2095 /* Make sure that MDI system buttons stay on the right side.
2096 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2097 * regardless of their id.
2099 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2100 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2103 TRACE("inserting at %u by pos %u\n", pos
, flags
& MF_BYPOSITION
);
2105 /* Create new items array */
2107 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2110 WARN("allocation failed\n" );
2113 if (menu
->nItems
> 0)
2115 /* Copy the old array into the new one */
2116 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2117 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2118 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2119 HeapFree( GetProcessHeap(), 0, menu
->items
);
2121 menu
->items
= newItems
;
2123 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2124 menu
->Height
= 0; /* force size recalculate */
2125 return &newItems
[pos
];
2129 /**********************************************************************
2130 * MENU_ParseResource
2132 * Parse a standard menu resource and add items to the menu.
2133 * Return a pointer to the end of the resource.
2135 * NOTE: flags is equivalent to the mtOption field
2137 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2145 flags
= GET_WORD(res
);
2146 end_flag
= flags
& MF_END
;
2147 /* Remove MF_END because it has the same value as MF_HILITE */
2149 res
+= sizeof(WORD
);
2150 if (!(flags
& MF_POPUP
))
2153 res
+= sizeof(WORD
);
2156 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2157 if (flags
& MF_POPUP
)
2159 HMENU hSubMenu
= CreatePopupMenu();
2160 if (!hSubMenu
) return NULL
;
2161 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2162 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2164 else /* Not a popup */
2166 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2168 } while (!end_flag
);
2173 /**********************************************************************
2174 * MENUEX_ParseResource
2176 * Parse an extended menu resource and add items to the menu.
2177 * Return a pointer to the end of the resource.
2179 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2185 mii
.cbSize
= sizeof(mii
);
2186 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2187 mii
.fType
= GET_DWORD(res
);
2188 res
+= sizeof(DWORD
);
2189 mii
.fState
= GET_DWORD(res
);
2190 res
+= sizeof(DWORD
);
2191 mii
.wID
= GET_DWORD(res
);
2192 res
+= sizeof(DWORD
);
2193 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2194 res
+= sizeof(WORD
);
2195 /* Align the text on a word boundary. */
2196 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2197 mii
.dwTypeData
= (LPWSTR
) res
;
2198 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2199 /* Align the following fields on a dword boundary. */
2200 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2202 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2203 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2205 if (resinfo
& 1) { /* Pop-up? */
2206 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2207 res
+= sizeof(DWORD
);
2208 mii
.hSubMenu
= CreatePopupMenu();
2211 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2212 DestroyMenu(mii
.hSubMenu
);
2215 mii
.fMask
|= MIIM_SUBMENU
;
2216 mii
.fType
|= MF_POPUP
;
2218 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2220 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2221 mii
.wID
, mii
.fType
);
2222 mii
.fType
|= MF_SEPARATOR
;
2224 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2225 } while (!(resinfo
& MF_END
));
2230 /***********************************************************************
2233 * Return the handle of the selected sub-popup menu (if any).
2235 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2240 menu
= MENU_GetMenu( hmenu
);
2242 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2244 item
= &menu
->items
[menu
->FocusedItem
];
2245 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2246 return item
->hSubMenu
;
2251 /***********************************************************************
2252 * MENU_HideSubPopups
2254 * Hide the sub-popup menus of this menu.
2256 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2257 BOOL sendMenuSelect
, UINT wFlags
)
2259 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2261 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2263 if (menu
&& top_popup
)
2269 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2271 item
= &menu
->items
[menu
->FocusedItem
];
2272 if (!(item
->fType
& MF_POPUP
) ||
2273 !(item
->fState
& MF_MOUSESELECT
)) return;
2274 item
->fState
&= ~MF_MOUSESELECT
;
2275 hsubmenu
= item
->hSubMenu
;
2278 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2279 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2280 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2281 DestroyWindow( submenu
->hWnd
);
2284 if (!(wFlags
& TPM_NONOTIFY
))
2285 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2286 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2291 /***********************************************************************
2294 * Display the sub-menu of the selected item of this menu.
2295 * Return the handle of the submenu, or hmenu if no submenu to display.
2297 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2298 BOOL selectFirst
, UINT wFlags
)
2305 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2307 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2309 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2311 item
= &menu
->items
[menu
->FocusedItem
];
2312 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2315 /* message must be sent before using item,
2316 because nearly everything may be changed by the application ! */
2318 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2319 if (!(wFlags
& TPM_NONOTIFY
))
2320 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2321 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2323 item
= &menu
->items
[menu
->FocusedItem
];
2326 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2327 if (!(item
->fState
& MF_HILITE
))
2329 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2330 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2332 SelectObject( hdc
, get_menu_font(FALSE
));
2334 item
->fState
|= MF_HILITE
;
2335 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2336 ReleaseDC( menu
->hWnd
, hdc
);
2338 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2341 item
->fState
|= MF_MOUSESELECT
;
2343 if (IS_SYSTEM_MENU(menu
))
2345 MENU_InitSysMenuPopup(item
->hSubMenu
,
2346 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2347 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2349 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2350 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2351 rect
.top
= rect
.bottom
;
2352 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2353 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2357 GetWindowRect( menu
->hWnd
, &rect
);
2358 if (menu
->wFlags
& MF_POPUP
)
2360 RECT rc
= item
->rect
;
2362 MENU_AdjustMenuItemRect(menu
, &rc
);
2364 /* The first item in the popup menu has to be at the
2365 same y position as the focused menu item */
2366 if (wFlags
& TPM_LAYOUTRTL
)
2367 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2369 rect
.left
+= rc
.right
- GetSystemMetrics(SM_CXBORDER
);
2370 rect
.top
+= rc
.top
- MENU_TOP_MARGIN
;
2371 rect
.right
= rc
.left
- rc
.right
+ GetSystemMetrics(SM_CXBORDER
);
2372 rect
.bottom
= rc
.top
- rc
.bottom
- MENU_TOP_MARGIN
2373 - MENU_BOTTOM_MARGIN
- GetSystemMetrics(SM_CYBORDER
);
2377 if (wFlags
& TPM_LAYOUTRTL
)
2378 rect
.left
= rect
.right
- item
->rect
.left
;
2380 rect
.left
+= item
->rect
.left
;
2381 rect
.top
+= item
->rect
.bottom
;
2382 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2383 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2387 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2388 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2390 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2391 return item
->hSubMenu
;
2396 /**********************************************************************
2399 HWND
MENU_IsMenuActive(void)
2404 /**********************************************************************
2407 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2409 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2411 void MENU_EndMenu( HWND hwnd
)
2414 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2415 if (menu
&& hwnd
== menu
->hwndOwner
) EndMenu();
2418 /***********************************************************************
2421 * Walks menu chain trying to find a menu pt maps to.
2423 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2425 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2426 UINT item
= menu
->FocusedItem
;
2429 /* try subpopup first (if any) */
2430 ret
= (item
!= NO_SELECTED_ITEM
&&
2431 (menu
->items
[item
].fType
& MF_POPUP
) &&
2432 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2433 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2435 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2437 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2438 if( menu
->wFlags
& MF_POPUP
)
2440 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2442 else if (ht
== HTSYSMENU
)
2443 ret
= get_win_sys_menu( menu
->hWnd
);
2444 else if (ht
== HTMENU
)
2445 ret
= GetMenu( menu
->hWnd
);
2450 /***********************************************************************
2451 * MENU_ExecFocusedItem
2453 * Execute a menu item (for instance when user pressed Enter).
2454 * Return the wID of the executed item. Otherwise, -1 indicating
2455 * that no menu item was executed, -2 if a popup is shown;
2456 * Have to receive the flags for the TrackPopupMenu options to avoid
2457 * sending unwanted message.
2460 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2463 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2465 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2467 if (!menu
|| !menu
->nItems
||
2468 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2470 item
= &menu
->items
[menu
->FocusedItem
];
2472 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2474 if (!(item
->fType
& MF_POPUP
))
2476 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2478 /* If TPM_RETURNCMD is set you return the id, but
2479 do not send a message to the owner */
2480 if(!(wFlags
& TPM_RETURNCMD
))
2482 if( menu
->wFlags
& MF_SYSMENU
)
2483 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2484 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2487 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2488 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2490 if (dwStyle
& MNS_NOTIFYBYPOS
)
2491 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2494 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2502 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2509 /***********************************************************************
2510 * MENU_SwitchTracking
2512 * Helper function for menu navigation routines.
2514 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2516 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2517 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2519 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2521 if( pmt
->hTopMenu
!= hPtMenu
&&
2522 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2524 /* both are top level menus (system and menu-bar) */
2525 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2526 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2527 pmt
->hTopMenu
= hPtMenu
;
2529 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2530 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2534 /***********************************************************************
2537 * Return TRUE if we can go on with menu tracking.
2539 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2541 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2546 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2549 if( IS_SYSTEM_MENU(ptmenu
) )
2550 item
= ptmenu
->items
;
2552 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2556 if( ptmenu
->FocusedItem
!= id
)
2557 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2559 /* If the popup menu is not already "popped" */
2560 if(!(item
->fState
& MF_MOUSESELECT
))
2562 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2567 /* Else the click was on the menu bar, finish the tracking */
2572 /***********************************************************************
2575 * Return the value of MENU_ExecFocusedItem if
2576 * the selected item was not a popup. Else open the popup.
2577 * A -1 return value indicates that we go on with menu tracking.
2580 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2582 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2587 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2590 if( IS_SYSTEM_MENU(ptmenu
) )
2591 item
= ptmenu
->items
;
2593 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2595 if( item
&& (ptmenu
->FocusedItem
== id
))
2597 debug_print_menuitem ("FocusedItem: ", item
, "");
2599 if( !(item
->fType
& MF_POPUP
) )
2601 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2602 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2603 return executedMenuId
;
2606 /* If we are dealing with the top-level menu */
2607 /* and this is a click on an already "popped" item: */
2608 /* Stop the menu tracking and close the opened submenus */
2609 if((pmt
->hTopMenu
== hPtMenu
) && ptmenu
->bTimeToHide
)
2612 ptmenu
->bTimeToHide
= TRUE
;
2618 /***********************************************************************
2621 * Return TRUE if we can go on with menu tracking.
2623 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2625 UINT id
= NO_SELECTED_ITEM
;
2626 POPUPMENU
*ptmenu
= NULL
;
2630 ptmenu
= MENU_GetMenu( hPtMenu
);
2631 if( IS_SYSTEM_MENU(ptmenu
) )
2634 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2637 if( id
== NO_SELECTED_ITEM
)
2639 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2640 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2643 else if( ptmenu
->FocusedItem
!= id
)
2645 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2646 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2652 /***********************************************************************
2655 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2657 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2659 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2662 /* When skipping left, we need to do something special after the
2664 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2668 /* When skipping right, for the non-system menu, we need to
2669 handle the last non-special menu item (ie skip any window
2670 icons such as MDI maximize, restore or close) */
2671 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2673 UINT i
= menu
->FocusedItem
+ 1;
2674 while (i
< menu
->nItems
) {
2675 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2676 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2680 if (i
== menu
->nItems
) {
2684 /* When skipping right, we need to cater for the system menu */
2685 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2687 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2694 MDINEXTMENU next_menu
;
2699 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2700 next_menu
.hmenuNext
= 0;
2701 next_menu
.hwndNext
= 0;
2702 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2704 TRACE("%p [%p] -> %p [%p]\n",
2705 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2707 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2709 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2710 hNewWnd
= pmt
->hOwnerWnd
;
2711 if( IS_SYSTEM_MENU(menu
) )
2713 /* switch to the menu bar */
2715 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2719 menu
= MENU_GetMenu( hNewMenu
);
2720 id
= menu
->nItems
- 1;
2722 /* Skip backwards over any system predefined icons,
2723 eg. MDI close, restore etc icons */
2725 (menu
->items
[id
].wID
>= SC_SIZE
&&
2726 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2729 else if (style
& WS_SYSMENU
)
2731 /* switch to the system menu */
2732 hNewMenu
= get_win_sys_menu( hNewWnd
);
2736 else /* application returned a new menu to switch to */
2738 hNewMenu
= next_menu
.hmenuNext
;
2739 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2741 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2743 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2745 if (style
& WS_SYSMENU
&&
2746 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2748 /* get the real system menu */
2749 hNewMenu
= get_win_sys_menu(hNewWnd
);
2751 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2753 /* FIXME: Not sure what to do here;
2754 * perhaps try to track hNewMenu as a popup? */
2756 TRACE(" -- got confused.\n");
2763 if( hNewMenu
!= pmt
->hTopMenu
)
2765 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2767 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2768 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2771 if( hNewWnd
!= pmt
->hOwnerWnd
)
2773 pmt
->hOwnerWnd
= hNewWnd
;
2774 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2777 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2778 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2785 /***********************************************************************
2788 * The idea is not to show the popup if the next input message is
2789 * going to hide it anyway.
2791 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2795 msg
.hwnd
= pmt
->hOwnerWnd
;
2797 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2798 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2803 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2804 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2806 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2807 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2808 if( msg
.message
== WM_KEYDOWN
&&
2809 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2811 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2818 /* failures go through this */
2819 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2823 /***********************************************************************
2826 * Handle a VK_ESCAPE key event in a menu.
2828 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2830 BOOL bEndMenu
= TRUE
;
2832 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2834 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2836 if (menu
->wFlags
& MF_POPUP
)
2838 HMENU hmenutmp
, hmenuprev
;
2840 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2842 /* close topmost popup */
2843 while (hmenutmp
!= pmt
->hCurrentMenu
)
2845 hmenuprev
= hmenutmp
;
2846 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2849 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2850 pmt
->hCurrentMenu
= hmenuprev
;
2858 /***********************************************************************
2861 * Handle a VK_LEFT key event in a menu.
2863 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2866 HMENU hmenutmp
, hmenuprev
;
2869 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2870 menu
= MENU_GetMenu( hmenutmp
);
2872 /* Try to move 1 column left (if possible) */
2873 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2874 NO_SELECTED_ITEM
) {
2876 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2881 /* close topmost popup */
2882 while (hmenutmp
!= pmt
->hCurrentMenu
)
2884 hmenuprev
= hmenutmp
;
2885 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2888 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2889 pmt
->hCurrentMenu
= hmenuprev
;
2891 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2893 /* move menu bar selection if no more popups are left */
2895 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2896 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2898 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2900 /* A sublevel menu was displayed - display the next one
2901 * unless there is another displacement coming up */
2903 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2904 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2905 pmt
->hTopMenu
, TRUE
, wFlags
);
2911 /***********************************************************************
2914 * Handle a VK_RIGHT key event in a menu.
2916 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2919 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2922 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2924 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2925 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2927 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2929 /* If already displaying a popup, try to display sub-popup */
2931 hmenutmp
= pmt
->hCurrentMenu
;
2932 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2934 /* if subpopup was displayed then we are done */
2935 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2938 /* Check to see if there's another column */
2939 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2940 NO_SELECTED_ITEM
) {
2941 TRACE("Going to %d.\n", nextcol
);
2942 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2947 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2949 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2951 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2952 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2953 } else hmenutmp
= 0;
2955 /* try to move to the next item */
2956 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
2957 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2959 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2960 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2961 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2962 pmt
->hTopMenu
, TRUE
, wFlags
);
2966 static void CALLBACK
release_capture( BOOL __normal
)
2968 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
2971 /***********************************************************************
2974 * Menu tracking code.
2976 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2977 HWND hwnd
, const RECT
*lprect
)
2982 INT executedMenuId
= -1;
2984 BOOL enterIdleSent
= FALSE
;
2988 mt
.hCurrentMenu
= hmenu
;
2989 mt
.hTopMenu
= hmenu
;
2990 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2994 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
2995 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
2998 if (!(menu
= MENU_GetMenu( hmenu
)))
3000 WARN("Invalid menu handle %p\n", hmenu
);
3001 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3005 if (wFlags
& TPM_BUTTONDOWN
)
3007 /* Get the result in order to start the tracking or not */
3008 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3009 fEndMenu
= !fRemove
;
3012 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3014 /* owner may not be visible when tracking a popup, so use the menu itself */
3015 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3016 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3018 __TRY
while (!fEndMenu
)
3020 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3021 if (!menu
) /* sometimes happens if I do a window manager close */
3024 /* we have to keep the message in the queue until it's
3025 * clear that menu loop is not over yet. */
3029 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3031 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3032 /* remove the message from the queue */
3033 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3039 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3040 enterIdleSent
= TRUE
;
3041 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3047 /* check if EndMenu() tried to cancel us, by posting this message */
3048 if(msg
.message
== WM_CANCELMODE
)
3050 /* we are now out of the loop */
3053 /* remove the message from the queue */
3054 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3056 /* break out of internal loop, ala ESCAPE */
3060 TranslateMessage( &msg
);
3063 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3064 enterIdleSent
=FALSE
;
3067 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3070 * Use the mouse coordinates in lParam instead of those in the MSG
3071 * struct to properly handle synthetic messages. They are already
3072 * in screen coordinates.
3074 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3075 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3077 /* Find a menu for this mouse event */
3078 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3082 /* no WM_NC... messages in captured state */
3084 case WM_RBUTTONDBLCLK
:
3085 case WM_RBUTTONDOWN
:
3086 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3088 case WM_LBUTTONDBLCLK
:
3089 case WM_LBUTTONDOWN
:
3090 /* If the message belongs to the menu, removes it from the queue */
3091 /* Else, end menu tracking */
3092 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
3093 fEndMenu
= !fRemove
;
3097 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3100 /* Check if a menu was selected by the mouse */
3103 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3104 TRACE("executedMenuId %d\n", executedMenuId
);
3106 /* End the loop if executedMenuId is an item ID */
3107 /* or if the job was done (executedMenuId = 0). */
3108 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3110 /* No menu was selected by the mouse */
3111 /* if the function was called by TrackPopupMenu, continue
3112 with the menu tracking. If not, stop it */
3114 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
3119 /* the selected menu item must be changed every time */
3120 /* the mouse moves. */
3123 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3125 } /* switch(msg.message) - mouse */
3127 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3129 fRemove
= TRUE
; /* Keyboard messages are always removed */
3143 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3144 NO_SELECTED_ITEM
, FALSE
, 0 );
3145 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3146 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3150 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3152 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3153 if (!(menu
->wFlags
& MF_POPUP
))
3154 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3155 else /* otherwise try to move selection */
3156 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3157 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3161 MENU_KeyLeft( &mt
, wFlags
);
3165 MENU_KeyRight( &mt
, wFlags
);
3169 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3175 hi
.cbSize
= sizeof(HELPINFO
);
3176 hi
.iContextType
= HELPINFO_MENUITEM
;
3177 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3180 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3181 hi
.hItemHandle
= hmenu
;
3182 hi
.dwContextId
= menu
->dwContextHelpID
;
3183 hi
.MousePos
= msg
.pt
;
3184 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3191 break; /* WM_KEYDOWN */
3198 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3200 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3201 fEndMenu
= (executedMenuId
!= -2);
3206 /* Hack to avoid control chars. */
3207 /* We will find a better way real soon... */
3208 if (msg
.wParam
< 32) break;
3210 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3211 LOWORD(msg
.wParam
), FALSE
);
3212 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3213 else if (pos
== (UINT
)-1) MessageBeep(0);
3216 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3218 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3219 fEndMenu
= (executedMenuId
!= -2);
3223 } /* switch(msg.message) - kbd */
3227 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3228 DispatchMessageW( &msg
);
3232 if (!fEndMenu
) fRemove
= TRUE
;
3234 /* finally remove message from the queue */
3236 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3237 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3238 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3240 __FINALLY( release_capture
)
3242 /* If dropdown is still painted and the close box is clicked on
3243 then the menu will be destroyed as part of the DispatchMessage above.
3244 This will then invalidate the menu handle in mt.hTopMenu. We should
3245 check for this first. */
3246 if( IsMenu( mt
.hTopMenu
) )
3248 menu
= MENU_GetMenu( mt
.hTopMenu
);
3250 if( IsWindow( mt
.hOwnerWnd
) )
3252 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3254 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3256 DestroyWindow( menu
->hWnd
);
3259 if (!(wFlags
& TPM_NONOTIFY
))
3260 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3261 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3263 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3264 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3267 /* Reset the variable for hiding menu */
3268 if( menu
) menu
->bTimeToHide
= FALSE
;
3271 /* The return value is only used by TrackPopupMenu */
3272 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3273 if (executedMenuId
== -1) executedMenuId
= 0;
3274 return executedMenuId
;
3277 /***********************************************************************
3280 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3284 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3288 /* This makes the menus of applications built with Delphi work.
3289 * It also enables menus to be displayed in more than one window,
3290 * but there are some bugs left that need to be fixed in this case.
3292 if ((menu
= MENU_GetMenu( hMenu
))) menu
->hWnd
= hWnd
;
3294 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3295 if (!(wFlags
& TPM_NONOTIFY
))
3296 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3298 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3300 if (!(wFlags
& TPM_NONOTIFY
))
3302 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3303 /* If an app changed/recreated menu bar entries in WM_INITMENU
3304 * menu sizes will be recalculated once the menu created/shown.
3311 /***********************************************************************
3314 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3316 TRACE("hwnd=%p\n", hWnd
);
3318 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3321 top_popup_hmenu
= NULL
;
3325 /***********************************************************************
3326 * MENU_TrackMouseMenuBar
3328 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3330 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3332 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3333 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3335 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3337 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3340 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3341 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3342 MENU_ExitTracking(hWnd
, FALSE
);
3347 /***********************************************************************
3348 * MENU_TrackKbdMenuBar
3350 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3352 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3354 UINT uItem
= NO_SELECTED_ITEM
;
3356 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3358 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3360 /* find window that has a menu */
3362 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd
, GWL_STYLE
)))
3363 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3365 /* check if we have to track a system menu */
3367 hTrackMenu
= GetMenu( hwnd
);
3368 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3370 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3371 hTrackMenu
= get_win_sys_menu( hwnd
);
3373 wParam
|= HTSYSMENU
; /* prevent item lookup */
3375 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3377 if (!IsMenu( hTrackMenu
)) return;
3379 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3381 if( wChar
&& wChar
!= ' ' )
3383 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3384 if ( uItem
>= (UINT
)(-2) )
3386 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3387 /* schedule end of menu tracking */
3388 wFlags
|= TF_ENDMENU
;
3393 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3395 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3397 if( uItem
== NO_SELECTED_ITEM
)
3398 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3400 PostMessageW( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3404 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3405 MENU_ExitTracking( hwnd
, FALSE
);
3408 /**********************************************************************
3409 * TrackPopupMenuEx (USER32.@)
3411 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3412 HWND hWnd
, LPTPMPARAMS lpTpm
)
3417 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3418 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3419 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3421 /* Parameter check */
3422 /* FIXME: this check is performed several times, here and in the called
3423 functions. That could be optimized */
3424 if (!(menu
= MENU_GetMenu( hMenu
)))
3426 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3430 if (IsWindow(menu
->hWnd
))
3432 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3436 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3438 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3439 if (!(wFlags
& TPM_NONOTIFY
))
3440 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3442 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3443 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3444 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3445 MENU_ExitTracking(hWnd
, TRUE
);
3450 /**********************************************************************
3451 * TrackPopupMenu (USER32.@)
3453 * Like the win32 API, the function return the command ID only if the
3454 * flag TPM_RETURNCMD is on.
3457 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3458 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3460 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3463 /***********************************************************************
3466 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3468 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3470 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3476 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3477 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3481 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3482 return MA_NOACTIVATE
;
3487 BeginPaint( hwnd
, &ps
);
3488 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3489 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3490 EndPaint( hwnd
, &ps
);
3494 case WM_PRINTCLIENT
:
3496 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3497 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3505 /* zero out global pointer in case resident popup window was destroyed. */
3506 if (hwnd
== top_popup
) {
3508 top_popup_hmenu
= NULL
;
3516 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3519 SetWindowLongPtrW( hwnd
, 0, 0 );
3522 case MM_SETMENUHANDLE
:
3523 SetWindowLongPtrW( hwnd
, 0, wParam
);
3526 case MM_GETMENUHANDLE
:
3528 return GetWindowLongPtrW( hwnd
, 0 );
3531 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3537 /***********************************************************************
3538 * MENU_GetMenuBarHeight
3540 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3542 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3543 INT orgX
, INT orgY
)
3549 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3551 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3553 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3554 SelectObject( hdc
, get_menu_font(FALSE
));
3555 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3556 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3557 ReleaseDC( hwnd
, hdc
);
3558 return lppop
->Height
;
3562 /*******************************************************************
3563 * ChangeMenuA (USER32.@)
3565 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3566 UINT id
, UINT flags
)
3568 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3569 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3571 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3572 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3574 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3575 flags
& MF_BYPOSITION
? pos
: id
,
3576 flags
& ~MF_REMOVE
);
3577 /* Default: MF_INSERT */
3578 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3582 /*******************************************************************
3583 * ChangeMenuW (USER32.@)
3585 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3586 UINT id
, UINT flags
)
3588 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3589 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3591 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3592 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3594 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3595 flags
& MF_BYPOSITION
? pos
: id
,
3596 flags
& ~MF_REMOVE
);
3597 /* Default: MF_INSERT */
3598 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3602 /*******************************************************************
3603 * CheckMenuItem (USER32.@)
3605 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3610 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3611 ret
= item
->fState
& MF_CHECKED
;
3612 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3613 else item
->fState
&= ~MF_CHECKED
;
3618 /**********************************************************************
3619 * EnableMenuItem (USER32.@)
3621 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3627 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3629 /* Get the Popupmenu to access the owner menu */
3630 if (!(menu
= MENU_GetMenu(hMenu
)))
3633 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3636 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3637 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3639 /* If the close item in the system menu change update the close button */
3640 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3642 if (menu
->hSysMenuOwner
!= 0)
3645 POPUPMENU
* parentMenu
;
3647 /* Get the parent menu to access*/
3648 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3651 /* Refresh the frame to reflect the change */
3652 WIN_GetRectangles( parentMenu
->hWnd
, COORDS_CLIENT
, &rc
, NULL
);
3654 RedrawWindow(parentMenu
->hWnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3662 /*******************************************************************
3663 * GetMenuStringA (USER32.@)
3665 INT WINAPI
GetMenuStringA(
3666 HMENU hMenu
, /* [in] menuhandle */
3667 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3668 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3669 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3670 UINT wFlags
/* [in] MF_ flags */
3674 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3675 if (str
&& nMaxSiz
) str
[0] = '\0';
3676 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3677 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3680 if (!item
->text
) return 0;
3681 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3682 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3684 TRACE("returning %s\n", debugstr_a(str
));
3689 /*******************************************************************
3690 * GetMenuStringW (USER32.@)
3692 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3693 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3697 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3698 if (str
&& nMaxSiz
) str
[0] = '\0';
3699 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) {
3700 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3703 if (!str
|| !nMaxSiz
) return item
->text
? strlenW(item
->text
) : 0;
3704 if( !(item
->text
)) {
3708 lstrcpynW( str
, item
->text
, nMaxSiz
);
3709 TRACE("returning %s\n", debugstr_w(str
));
3710 return strlenW(str
);
3714 /**********************************************************************
3715 * HiliteMenuItem (USER32.@)
3717 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3721 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3722 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3723 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3724 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3725 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
, 0 );
3726 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3731 /**********************************************************************
3732 * GetMenuState (USER32.@)
3734 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3737 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3738 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3739 debug_print_menuitem (" item: ", item
, "");
3740 if (item
->fType
& MF_POPUP
)
3742 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3743 if (!menu
) return -1;
3744 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3748 /* We used to (from way back then) mask the result to 0xff. */
3749 /* I don't know why and it seems wrong as the documented */
3750 /* return flag MF_SEPARATOR is outside that mask. */
3751 return (item
->fType
| item
->fState
);
3756 /**********************************************************************
3757 * GetMenuItemCount (USER32.@)
3759 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3761 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3762 if (!menu
) return -1;
3763 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3764 return menu
->nItems
;
3768 /**********************************************************************
3769 * GetMenuItemID (USER32.@)
3771 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3775 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return -1;
3776 if (lpmi
->fType
& MF_POPUP
) return -1;
3782 /**********************************************************************
3785 * Uses flags, id and text ptr, passed by InsertMenu() and
3786 * ModifyMenu() to setup a MenuItemInfo structure.
3788 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3789 LPMENUITEMINFOW pmii
)
3791 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3792 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3793 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3794 /* setting bitmap clears text and vice versa */
3795 if( IS_STRING_ITEM(flags
)) {
3796 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3798 flags
|= MF_SEPARATOR
;
3799 /* Item beginning with a backspace is a help item */
3800 /* FIXME: wrong place, this is only true in win16 */
3801 else if( *str
== '\b') {
3805 pmii
->dwTypeData
= (LPWSTR
)str
;
3806 } else if( flags
& MFT_BITMAP
){
3807 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3808 pmii
->hbmpItem
= ULongToHandle(LOWORD(str
));
3810 if( flags
& MF_OWNERDRAW
){
3811 pmii
->fMask
|= MIIM_DATA
;
3812 pmii
->dwItemData
= (ULONG_PTR
) str
;
3814 if( flags
& MF_POPUP
) {
3815 pmii
->fMask
|= MIIM_SUBMENU
;
3816 pmii
->hSubMenu
= (HMENU
)id
;
3818 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3819 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3820 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3821 pmii
->wID
= (UINT
)id
;
3825 /*******************************************************************
3826 * InsertMenuW (USER32.@)
3828 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3829 UINT_PTR id
, LPCWSTR str
)
3834 if (IS_STRING_ITEM(flags
) && str
)
3835 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3836 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3837 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3838 hMenu
, pos
, flags
, id
, str
);
3840 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3841 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3842 if (!(SetMenuItemInfo_common( item
, &mii
, TRUE
)))
3844 RemoveMenu( hMenu
, pos
, flags
);
3848 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3853 /*******************************************************************
3854 * InsertMenuA (USER32.@)
3856 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3857 UINT_PTR id
, LPCSTR str
)
3861 if (IS_STRING_ITEM(flags
) && str
)
3863 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3864 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3867 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3868 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3869 HeapFree( GetProcessHeap(), 0, newstr
);
3873 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3877 /*******************************************************************
3878 * AppendMenuA (USER32.@)
3880 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3881 UINT_PTR id
, LPCSTR data
)
3883 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3887 /*******************************************************************
3888 * AppendMenuW (USER32.@)
3890 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3891 UINT_PTR id
, LPCWSTR data
)
3893 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3897 /**********************************************************************
3898 * RemoveMenu (USER32.@)
3900 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3905 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3906 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3907 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3911 MENU_FreeItemData( item
);
3913 if (--menu
->nItems
== 0)
3915 HeapFree( GetProcessHeap(), 0, menu
->items
);
3920 while(nPos
< menu
->nItems
)
3926 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3927 menu
->nItems
* sizeof(MENUITEM
) );
3933 /**********************************************************************
3934 * DeleteMenu (USER32.@)
3936 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3938 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3939 if (!item
) return FALSE
;
3940 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3941 /* nPos is now the position of the item */
3942 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3947 /*******************************************************************
3948 * ModifyMenuW (USER32.@)
3950 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3951 UINT_PTR id
, LPCWSTR str
)
3956 if (IS_STRING_ITEM(flags
))
3957 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3959 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
3961 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3962 MENU_GetMenu(hMenu
)->Height
= 0; /* force size recalculate */
3963 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
3964 return SetMenuItemInfo_common( item
, &mii
, TRUE
);
3968 /*******************************************************************
3969 * ModifyMenuA (USER32.@)
3971 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3972 UINT_PTR id
, LPCSTR str
)
3976 if (IS_STRING_ITEM(flags
) && str
)
3978 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3979 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3982 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3983 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3984 HeapFree( GetProcessHeap(), 0, newstr
);
3988 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3992 /**********************************************************************
3993 * CreatePopupMenu (USER32.@)
3995 HMENU WINAPI
CreatePopupMenu(void)
4000 if (!(hmenu
= CreateMenu())) return 0;
4001 menu
= MENU_GetMenu( hmenu
);
4002 menu
->wFlags
|= MF_POPUP
;
4003 menu
->bTimeToHide
= FALSE
;
4008 /**********************************************************************
4009 * GetMenuCheckMarkDimensions (USER.417)
4010 * GetMenuCheckMarkDimensions (USER32.@)
4012 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4014 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4018 /**********************************************************************
4019 * SetMenuItemBitmaps (USER32.@)
4021 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4022 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4026 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
4028 if (!hNewCheck
&& !hNewUnCheck
)
4030 item
->fState
&= ~MF_USECHECKBITMAPS
;
4032 else /* Install new bitmaps */
4034 item
->hCheckBit
= hNewCheck
;
4035 item
->hUnCheckBit
= hNewUnCheck
;
4036 item
->fState
|= MF_USECHECKBITMAPS
;
4042 /**********************************************************************
4043 * CreateMenu (USER32.@)
4045 HMENU WINAPI
CreateMenu(void)
4050 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4051 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4052 menu
->bTimeToHide
= FALSE
;
4054 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4056 TRACE("return %p\n", hMenu
);
4062 /**********************************************************************
4063 * DestroyMenu (USER32.@)
4065 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4069 TRACE("(%p)\n", hMenu
);
4071 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4072 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4074 /* DestroyMenu should not destroy system menu popup owner */
4075 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4077 DestroyWindow( lppop
->hWnd
);
4081 if (lppop
->items
) /* recursively destroy submenus */
4084 MENUITEM
*item
= lppop
->items
;
4085 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4087 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4088 MENU_FreeItemData( item
);
4090 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4092 HeapFree( GetProcessHeap(), 0, lppop
);
4097 /**********************************************************************
4098 * GetSystemMenu (USER32.@)
4100 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4102 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4105 if (wndPtr
== WND_DESKTOP
) return 0;
4106 if (wndPtr
== WND_OTHER_PROCESS
)
4108 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4112 if (wndPtr
->hSysMenu
&& bRevert
)
4114 DestroyMenu(wndPtr
->hSysMenu
);
4115 wndPtr
->hSysMenu
= 0;
4118 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4119 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4121 if( wndPtr
->hSysMenu
)
4124 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4126 /* Store the dummy sysmenu handle to facilitate the refresh */
4127 /* of the close button if the SC_CLOSE item change */
4128 menu
= MENU_GetMenu(retvalue
);
4130 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4132 WIN_ReleasePtr( wndPtr
);
4134 return bRevert
? 0 : retvalue
;
4138 /*******************************************************************
4139 * SetSystemMenu (USER32.@)
4141 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4143 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4145 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4147 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4148 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4149 WIN_ReleasePtr( wndPtr
);
4156 /**********************************************************************
4157 * GetMenu (USER32.@)
4159 HMENU WINAPI
GetMenu( HWND hWnd
)
4161 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4162 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4166 /**********************************************************************
4167 * GetMenuBarInfo (USER32.@)
4169 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4171 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4175 /**********************************************************************
4178 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4179 * SetWindowPos call that would result if SetMenu were called directly.
4181 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4183 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4185 if (hMenu
&& !IsMenu(hMenu
))
4187 WARN("hMenu %p is not a menu handle\n", hMenu
);
4190 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4193 hWnd
= WIN_GetFullHandle( hWnd
);
4194 if (GetCapture() == hWnd
)
4195 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4201 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
4203 lpmenu
->hWnd
= hWnd
;
4204 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
4206 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4211 /**********************************************************************
4212 * SetMenu (USER32.@)
4214 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4216 if(!MENU_SetMenu(hWnd
, hMenu
))
4219 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4220 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4225 /**********************************************************************
4226 * GetSubMenu (USER32.@)
4228 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4232 if (!(lpmi
= MENU_FindItem(&hMenu
,(UINT
*)&nPos
,MF_BYPOSITION
))) return 0;
4233 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
4234 return lpmi
->hSubMenu
;
4238 /**********************************************************************
4239 * DrawMenuBar (USER32.@)
4241 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4244 HMENU hMenu
= GetMenu(hWnd
);
4246 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd
, GWL_STYLE
)))
4248 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
4250 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4251 lppop
->hwndOwner
= hWnd
;
4252 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4253 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4257 /***********************************************************************
4258 * DrawMenuBarTemp (USER32.@)
4262 * called by W98SE desk.cpl Control Panel Applet
4264 * Not 100% sure about the param names, but close.
4266 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4271 BOOL flat_menu
= FALSE
;
4273 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4276 hMenu
= GetMenu(hwnd
);
4279 hFont
= get_menu_font(FALSE
);
4281 lppop
= MENU_GetMenu( hMenu
);
4282 if (lppop
== NULL
|| lprect
== NULL
)
4284 retvalue
= GetSystemMetrics(SM_CYMENU
);
4288 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4290 hfontOld
= SelectObject( hDC
, hFont
);
4292 if (lppop
->Height
== 0)
4293 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4295 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4297 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4299 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4300 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4301 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4303 if (lppop
->nItems
== 0)
4305 retvalue
= GetSystemMetrics(SM_CYMENU
);
4309 for (i
= 0; i
< lppop
->nItems
; i
++)
4311 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
4312 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
4314 retvalue
= lppop
->Height
;
4317 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4321 /***********************************************************************
4322 * EndMenu (USER.187)
4323 * EndMenu (USER32.@)
4325 BOOL WINAPI
EndMenu(void)
4327 /* if we are in the menu code, and it is active */
4328 if (!fEndMenu
&& top_popup
)
4330 /* terminate the menu handling code */
4333 /* needs to be posted to wakeup the internal menu handler */
4334 /* which will now terminate the menu, in the event that */
4335 /* the main window was minimized, or lost focus, so we */
4336 /* don't end up with an orphaned menu */
4337 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4343 /*****************************************************************
4344 * LoadMenuA (USER32.@)
4346 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4348 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4349 if (!hrsrc
) return 0;
4350 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4354 /*****************************************************************
4355 * LoadMenuW (USER32.@)
4357 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4359 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4360 if (!hrsrc
) return 0;
4361 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4365 /**********************************************************************
4366 * LoadMenuIndirectW (USER32.@)
4368 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4371 WORD version
, offset
;
4372 LPCSTR p
= template;
4374 version
= GET_WORD(p
);
4376 TRACE("%p, ver %d\n", template, version
);
4379 case 0: /* standard format is version of 0 */
4380 offset
= GET_WORD(p
);
4381 p
+= sizeof(WORD
) + offset
;
4382 if (!(hMenu
= CreateMenu())) return 0;
4383 if (!MENU_ParseResource( p
, hMenu
))
4385 DestroyMenu( hMenu
);
4389 case 1: /* extended format is version of 1 */
4390 offset
= GET_WORD(p
);
4391 p
+= sizeof(WORD
) + offset
;
4392 if (!(hMenu
= CreateMenu())) return 0;
4393 if (!MENUEX_ParseResource( p
, hMenu
))
4395 DestroyMenu( hMenu
);
4400 ERR("version %d not supported.\n", version
);
4406 /**********************************************************************
4407 * LoadMenuIndirectA (USER32.@)
4409 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4411 return LoadMenuIndirectW( template );
4415 /**********************************************************************
4418 BOOL WINAPI
IsMenu(HMENU hmenu
)
4420 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4424 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4430 /**********************************************************************
4431 * GetMenuItemInfo_common
4434 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4435 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4437 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4439 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4442 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4446 if( lpmii
->fMask
& MIIM_TYPE
) {
4447 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4448 WARN("invalid combination of fMask bits used\n");
4449 /* this does not happen on Win9x/ME */
4450 SetLastError( ERROR_INVALID_PARAMETER
);
4453 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4454 if( menu
->hbmpItem
) lpmii
->fType
|= MFT_BITMAP
;
4455 lpmii
->hbmpItem
= menu
->hbmpItem
; /* not on Win9x/ME */
4456 if( lpmii
->fType
& MFT_BITMAP
) {
4457 lpmii
->dwTypeData
= (LPWSTR
) menu
->hbmpItem
;
4459 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4460 /* this does not happen on Win9x/ME */
4461 lpmii
->dwTypeData
= 0;
4466 /* copy the text string */
4467 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4469 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4472 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4474 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4480 len
= strlenW(menu
->text
);
4481 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4482 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4486 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
,
4487 0, NULL
, NULL
) - 1;
4488 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4489 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4490 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4491 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4493 /* if we've copied a substring we return its length */
4494 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4495 if (lpmii
->cch
<= len
+ 1)
4500 /* return length of string */
4501 /* not on Win9x/ME if fType & MFT_BITMAP */
4507 if (lpmii
->fMask
& MIIM_FTYPE
)
4508 lpmii
->fType
= menu
->fType
& MENUITEMINFO_TYPE_MASK
;
4510 if (lpmii
->fMask
& MIIM_BITMAP
)
4511 lpmii
->hbmpItem
= menu
->hbmpItem
;
4513 if (lpmii
->fMask
& MIIM_STATE
)
4514 lpmii
->fState
= menu
->fState
& MENUITEMINFO_STATE_MASK
;
4516 if (lpmii
->fMask
& MIIM_ID
)
4517 lpmii
->wID
= menu
->wID
;
4519 if (lpmii
->fMask
& MIIM_SUBMENU
)
4520 lpmii
->hSubMenu
= menu
->hSubMenu
;
4522 /* hSubMenu is always cleared
4523 * (not on Win9x/ME ) */
4524 lpmii
->hSubMenu
= 0;
4527 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4528 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4529 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4531 if (lpmii
->fMask
& MIIM_DATA
)
4532 lpmii
->dwItemData
= menu
->dwItemData
;
4537 /**********************************************************************
4538 * GetMenuItemInfoA (USER32.@)
4540 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4541 LPMENUITEMINFOA lpmii
)
4545 if( lpmii
->cbSize
!= sizeof( mii
) &&
4546 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4547 SetLastError( ERROR_INVALID_PARAMETER
);
4550 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4551 mii
.cbSize
= sizeof( mii
);
4552 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4553 (LPMENUITEMINFOW
)&mii
, FALSE
);
4554 mii
.cbSize
= lpmii
->cbSize
;
4555 memcpy( lpmii
, &mii
, mii
.cbSize
);
4559 /**********************************************************************
4560 * GetMenuItemInfoW (USER32.@)
4562 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4563 LPMENUITEMINFOW lpmii
)
4567 if( lpmii
->cbSize
!= sizeof( mii
) &&
4568 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4569 SetLastError( ERROR_INVALID_PARAMETER
);
4572 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4573 mii
.cbSize
= sizeof( mii
);
4574 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4575 mii
.cbSize
= lpmii
->cbSize
;
4576 memcpy( lpmii
, &mii
, mii
.cbSize
);
4581 /* set a menu item text from a ASCII or Unicode string */
4582 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4588 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4589 strcpyW( menu
->text
, text
);
4593 LPCSTR str
= (LPCSTR
)text
;
4594 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4595 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4596 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4601 /**********************************************************************
4604 * detect if there are loops in the menu tree (or the depth is too large)
4606 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4613 if( depth
> MAXMENUDEPTH
) return depth
;
4614 item
= pmenu
->items
;
4616 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4617 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4619 int bdepth
= MENU_depth( psubmenu
, depth
);
4620 if( bdepth
> subdepth
) subdepth
= bdepth
;
4622 if( subdepth
> MAXMENUDEPTH
)
4623 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4629 /**********************************************************************
4630 * SetMenuItemInfo_common
4632 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4633 * MIIM_BITMAP and MIIM_STRING flags instead.
4636 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4637 const MENUITEMINFOW
*lpmii
,
4640 if (!menu
) return FALSE
;
4642 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4644 if (lpmii
->fMask
& MIIM_FTYPE
) {
4645 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4646 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4648 if (lpmii
->fMask
& MIIM_STRING
) {
4649 /* free the string when used */
4650 HeapFree(GetProcessHeap(), 0, menu
->text
);
4651 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4654 if (lpmii
->fMask
& MIIM_STATE
)
4655 /* Other menu items having MFS_DEFAULT are not converted
4657 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4659 if (lpmii
->fMask
& MIIM_ID
)
4660 menu
->wID
= lpmii
->wID
;
4662 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4663 menu
->hSubMenu
= lpmii
->hSubMenu
;
4664 if (menu
->hSubMenu
) {
4665 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4667 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4668 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4672 subMenu
->wFlags
|= MF_POPUP
;
4673 menu
->fType
|= MF_POPUP
;
4675 SetLastError( ERROR_INVALID_PARAMETER
);
4680 menu
->fType
&= ~MF_POPUP
;
4683 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4685 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4686 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4688 if (lpmii
->fMask
& MIIM_DATA
)
4689 menu
->dwItemData
= lpmii
->dwItemData
;
4691 if (lpmii
->fMask
& MIIM_BITMAP
)
4692 menu
->hbmpItem
= lpmii
->hbmpItem
;
4694 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
4695 menu
->fType
|= MFT_SEPARATOR
;
4697 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4701 /**********************************************************************
4702 * MENU_NormalizeMenuItemInfoStruct
4704 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4705 * check, copy and extend the MENUITEMINFO struct from the version that the application
4706 * supplied to the version used by wine source. */
4707 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
4708 MENUITEMINFOW
*pmii_out
)
4710 /* do we recognize the size? */
4711 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
4712 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) {
4713 SetLastError( ERROR_INVALID_PARAMETER
);
4716 /* copy the fields that we have */
4717 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
4718 /* if the hbmpItem member is missing then extend */
4719 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
4720 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
4721 pmii_out
->hbmpItem
= NULL
;
4723 /* test for invalid bit combinations */
4724 if( (pmii_out
->fMask
& MIIM_TYPE
&&
4725 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
4726 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
4727 WARN("invalid combination of fMask bits used\n");
4728 /* this does not happen on Win9x/ME */
4729 SetLastError( ERROR_INVALID_PARAMETER
);
4732 /* convert old style (MIIM_TYPE) to the new */
4733 if( pmii_out
->fMask
& MIIM_TYPE
){
4734 pmii_out
->fMask
|= MIIM_FTYPE
;
4735 if( IS_STRING_ITEM(pmii_out
->fType
)){
4736 pmii_out
->fMask
|= MIIM_STRING
;
4737 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
4738 pmii_out
->fMask
|= MIIM_BITMAP
;
4739 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
4745 /**********************************************************************
4746 * SetMenuItemInfoA (USER32.@)
4748 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4749 const MENUITEMINFOA
*lpmii
)
4753 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4755 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4757 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4761 /**********************************************************************
4762 * SetMenuItemInfoW (USER32.@)
4764 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4765 const MENUITEMINFOW
*lpmii
)
4769 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
4771 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4772 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
,
4773 &item
, bypos
? MF_BYPOSITION
: 0), &mii
, TRUE
);
4776 /**********************************************************************
4777 * SetMenuDefaultItem (USER32.@)
4780 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4786 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4788 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4790 /* reset all default-item flags */
4792 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4794 item
->fState
&= ~MFS_DEFAULT
;
4797 /* no default item */
4806 if ( uItem
>= menu
->nItems
) return FALSE
;
4807 item
[uItem
].fState
|= MFS_DEFAULT
;
4812 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4814 if (item
->wID
== uItem
)
4816 item
->fState
|= MFS_DEFAULT
;
4825 /**********************************************************************
4826 * GetMenuDefaultItem (USER32.@)
4828 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4834 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4836 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4838 /* find default item */
4842 if (! item
) return -1;
4844 while ( !( item
->fState
& MFS_DEFAULT
) )
4847 if (i
>= menu
->nItems
) return -1;
4850 /* default: don't return disabled items */
4851 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4853 /* search rekursiv when needed */
4854 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4857 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4858 if ( -1 != ret
) return ret
;
4860 /* when item not found in submenu, return the popup item */
4862 return ( bypos
) ? i
: item
->wID
;
4867 /**********************************************************************
4868 * InsertMenuItemA (USER32.@)
4870 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4871 const MENUITEMINFOA
*lpmii
)
4876 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4878 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
4880 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4881 return SetMenuItemInfo_common(item
, &mii
, FALSE
);
4885 /**********************************************************************
4886 * InsertMenuItemW (USER32.@)
4888 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4889 const MENUITEMINFOW
*lpmii
)
4894 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
4896 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
4898 item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4899 return SetMenuItemInfo_common(item
, &mii
, TRUE
);
4902 /**********************************************************************
4903 * CheckMenuRadioItem (USER32.@)
4906 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4907 UINT first
, UINT last
, UINT check
,
4912 MENUITEM
*mi_first
= NULL
, *mi_check
;
4913 HMENU m_first
, m_check
;
4915 for (i
= first
; i
<= last
; i
++)
4922 mi_first
= MENU_FindItem(&m_first
, &pos
, bypos
);
4923 if (!mi_first
) continue;
4924 mi_check
= mi_first
;
4930 mi_check
= MENU_FindItem(&m_check
, &pos
, bypos
);
4931 if (!mi_check
) continue;
4934 if (m_first
!= m_check
) continue;
4935 if (mi_check
->fType
== MFT_SEPARATOR
) continue;
4939 mi_check
->fType
|= MFT_RADIOCHECK
;
4940 mi_check
->fState
|= MFS_CHECKED
;
4945 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4946 mi_check
->fState
&= ~MFS_CHECKED
;
4954 /**********************************************************************
4955 * GetMenuItemRect (USER32.@)
4957 * ATTENTION: Here, the returned values in rect are the screen
4958 * coordinates of the item just like if the menu was
4959 * always on the upper left side of the application.
4962 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4965 POPUPMENU
*itemMenu
;
4969 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4971 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4972 referenceHwnd
= hwnd
;
4976 itemMenu
= MENU_GetMenu(hMenu
);
4977 if (itemMenu
== NULL
)
4980 if(itemMenu
->hWnd
== 0)
4982 referenceHwnd
= itemMenu
->hWnd
;
4985 if ((rect
== NULL
) || (item
== NULL
))
4990 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4995 /**********************************************************************
4996 * SetMenuInfo (USER32.@)
4999 * actually use the items to draw the menu
5000 * (recalculate and/or redraw)
5002 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5005 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5007 if (lpmi
->fMask
& MIM_BACKGROUND
)
5008 menu
->hbrBack
= lpmi
->hbrBack
;
5010 if (lpmi
->fMask
& MIM_HELPID
)
5011 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5013 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5014 menu
->cyMax
= lpmi
->cyMax
;
5016 if (lpmi
->fMask
& MIM_MENUDATA
)
5017 menu
->dwMenuData
= lpmi
->dwMenuData
;
5019 if (lpmi
->fMask
& MIM_STYLE
)
5020 menu
->dwStyle
= lpmi
->dwStyle
;
5022 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5024 MENUITEM
*item
= menu
->items
;
5025 for( i
= menu
->nItems
; i
; i
--, item
++)
5026 if( item
->fType
& MF_POPUP
)
5027 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5032 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5034 TRACE("(%p %p)\n", hMenu
, lpmi
);
5035 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5036 if( lpmi
->fMask
& MIM_STYLE
) {
5037 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5038 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5039 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5043 SetLastError( ERROR_INVALID_PARAMETER
);
5047 /**********************************************************************
5048 * GetMenuInfo (USER32.@)
5054 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5057 TRACE("(%p %p)\n", hMenu
, lpmi
);
5059 if (lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
5062 if (lpmi
->fMask
& MIM_BACKGROUND
)
5063 lpmi
->hbrBack
= menu
->hbrBack
;
5065 if (lpmi
->fMask
& MIM_HELPID
)
5066 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5068 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5069 lpmi
->cyMax
= menu
->cyMax
;
5071 if (lpmi
->fMask
& MIM_MENUDATA
)
5072 lpmi
->dwMenuData
= menu
->dwMenuData
;
5074 if (lpmi
->fMask
& MIM_STYLE
)
5075 lpmi
->dwStyle
= menu
->dwStyle
;
5079 SetLastError( ERROR_INVALID_PARAMETER
);
5084 /**********************************************************************
5085 * SetMenuContextHelpId (USER32.@)
5087 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5091 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5093 if ((menu
= MENU_GetMenu(hMenu
)))
5095 menu
->dwContextHelpID
= dwContextHelpID
;
5102 /**********************************************************************
5103 * GetMenuContextHelpId (USER32.@)
5105 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5109 TRACE("(%p)\n", hMenu
);
5111 if ((menu
= MENU_GetMenu(hMenu
)))
5113 return menu
->dwContextHelpID
;
5118 /**********************************************************************
5119 * MenuItemFromPoint (USER32.@)
5121 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5123 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
5126 /*FIXME: Do we have to handle hWnd here? */
5127 if (!menu
) return -1;
5128 if (!MENU_FindItemByCoords(menu
, ptScreen
, &pos
)) return -1;
5133 /**********************************************************************
5134 * translate_accelerator
5136 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5137 BYTE fVirt
, WORD key
, WORD cmd
)
5142 if (wParam
!= key
) return FALSE
;
5144 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5145 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5146 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5148 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5150 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5152 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5158 if(fVirt
& FVIRTKEY
)
5160 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5161 wParam
, 0xff & HIWORD(lParam
));
5163 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5164 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5168 if (!(lParam
& 0x01000000)) /* no special_key */
5170 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5171 { /* ^^ ALT pressed */
5172 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5181 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5185 HMENU hMenu
, hSubMenu
, hSysMenu
;
5186 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5188 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5189 hSysMenu
= get_win_sys_menu( hWnd
);
5191 /* find menu item and ask application to initialize it */
5192 /* 1. in the system menu */
5193 hSubMenu
= hSysMenu
;
5195 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5199 if (!IsWindowEnabled(hWnd
))
5203 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5204 if(hSubMenu
!= hSysMenu
)
5206 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5207 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5208 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5210 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5213 else /* 2. in the window's menu */
5217 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
5221 if (!IsWindowEnabled(hWnd
))
5225 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5226 if(hSubMenu
!= hMenu
)
5228 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5229 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5230 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5232 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5239 if (uSysStat
!= (UINT
)-1)
5241 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5248 if (uStat
!= (UINT
)-1)
5254 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5266 if( mesg
==WM_COMMAND
)
5268 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5269 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5271 else if( mesg
==WM_SYSCOMMAND
)
5273 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5274 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5278 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5279 * #0: unknown (please report!)
5280 * #1: for WM_KEYUP,WM_SYSKEYUP
5281 * #2: mouse is captured
5282 * #3: window is disabled
5283 * #4: it's a disabled system menu option
5284 * #5: it's a menu option, but window is iconic
5285 * #6: it's a menu option, but disabled
5287 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5289 ERR_(accel
)(" unknown reason - please report!\n");
5294 /**********************************************************************
5295 * TranslateAcceleratorA (USER32.@)
5296 * TranslateAccelerator (USER32.@)
5298 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5300 switch (msg
->message
)
5304 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5310 char ch
= LOWORD(msg
->wParam
);
5312 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5313 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5314 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5322 /**********************************************************************
5323 * TranslateAcceleratorW (USER32.@)
5325 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5327 ACCEL data
[32], *ptr
= data
;
5330 if (!hWnd
) return 0;
5332 if (msg
->message
!= WM_KEYDOWN
&&
5333 msg
->message
!= WM_SYSKEYDOWN
&&
5334 msg
->message
!= WM_CHAR
&&
5335 msg
->message
!= WM_SYSCHAR
)
5338 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5339 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5341 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5342 if (count
> sizeof(data
)/sizeof(data
[0]))
5344 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5346 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5347 for (i
= 0; i
< count
; i
++)
5349 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5350 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5353 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);