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 /* Menu item structure */
67 /* ----------- MENUITEMINFO Stuff ----------- */
68 UINT fType
; /* Item type. */
69 UINT fState
; /* Item state. */
70 UINT_PTR wID
; /* Item id. */
71 HMENU hSubMenu
; /* Pop-up menu. */
72 HBITMAP hCheckBit
; /* Bitmap when checked. */
73 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
74 LPWSTR text
; /* Item text. */
75 ULONG_PTR dwItemData
; /* Application defined. */
76 LPWSTR dwTypeData
; /* depends on fMask */
77 HBITMAP hbmpItem
; /* bitmap */
78 /* ----------- Wine stuff ----------- */
79 RECT rect
; /* Item area (relative to the items_rect).
80 * See MENU_AdjustMenuItemRect(). */
81 UINT xTab
; /* X position of text after Tab */
82 SIZE bmpsize
; /* size needed for the HBMMENU_CALLBACK
86 /* Popup menu structure */
88 struct user_object obj
;
89 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
90 WORD Width
; /* Width of the whole menu */
91 WORD Height
; /* Height of the whole menu */
92 UINT nItems
; /* Number of items in the menu */
93 HWND hWnd
; /* Window containing the menu */
94 MENUITEM
*items
; /* Array of menu items */
95 UINT FocusedItem
; /* Currently focused item */
96 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
97 BOOL bScrolling
; /* Scroll arrows are active */
98 UINT nScrollPos
; /* Current scroll position */
99 UINT nTotalHeight
; /* Total height of menu items inside menu */
100 RECT items_rect
; /* Rectangle within which the items lie. Excludes margins and scroll arrows */
102 /* ------------ MENUINFO members ------ */
103 DWORD dwStyle
; /* Extended menu style */
104 UINT cyMax
; /* max height of the whole menu, 0 is screen height */
105 HBRUSH hbrBack
; /* brush for menu background */
106 DWORD dwContextHelpID
;
107 DWORD dwMenuData
; /* application defined value */
108 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
109 WORD textOffset
; /* Offset of text when items have both bitmaps and text */
110 } POPUPMENU
, *LPPOPUPMENU
;
112 /* internal flags for menu tracking */
114 #define TF_ENDMENU 0x10000
115 #define TF_SUSPENDPOPUP 0x20000
116 #define TF_SKIPREMOVE 0x40000
117 #define TF_RCVD_BTN_UP 0x80000
122 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
123 HMENU hTopMenu
; /* initial menu */
124 HWND hOwnerWnd
; /* where notifications are sent */
131 /* Internal MENU_TrackMenu() flags */
132 #define TPM_INTERNAL 0xF0000000
133 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
134 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
136 /* Space between 2 columns */
137 #define MENU_COL_SPACE 4
139 /* Margins for popup menus */
140 #define MENU_MARGIN 3
142 /* maximum allowed depth of any branch in the menu tree.
143 * This value is slightly larger than in windows (25) to
144 * stay on the safe side. */
145 #define MAXMENUDEPTH 30
147 /* (other menu->FocusedItem values give the position of the focused item) */
148 #define NO_SELECTED_ITEM 0xffff
150 #define MENU_ITEM_TYPE(flags) \
151 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
153 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
154 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
155 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
157 #define IS_SYSTEM_MENU(menu) \
158 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
160 #define MENUITEMINFO_TYPE_MASK \
161 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
162 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
163 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
164 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
165 #define STATE_MASK (~TYPE_MASK)
166 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
168 static SIZE menucharsize
;
169 static UINT ODitemheight
; /* default owner drawn item height */
171 /* Use global popup window because there's no way 2 menus can
172 * be tracked at the same time. */
173 static HWND top_popup
;
174 static HMENU top_popup_hmenu
;
176 /* Flag set by EndMenu() to force an exit from menu tracking */
177 static BOOL fEndMenu
= FALSE
;
179 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
181 static BOOL
SetMenuItemInfo_common( MENUITEM
*, const MENUITEMINFOW
*, BOOL
);
183 static BOOL
is_win_menu_disallowed(HWND hwnd
)
185 return (GetWindowLongW(hwnd
, GWL_STYLE
) & (WS_CHILD
| WS_POPUP
)) == WS_CHILD
;
188 /*********************************************************************
189 * menu class descriptor
191 const struct builtin_class_descr MENU_builtin_class
=
193 (LPCWSTR
)POPUPMENU_CLASS_ATOM
, /* name */
194 CS_DROPSHADOW
| CS_SAVEBITS
| CS_DBLCLKS
, /* style */
195 WINPROC_MENU
, /* proc */
196 sizeof(HMENU
), /* extra */
197 IDC_ARROW
, /* cursor */
198 (HBRUSH
)(COLOR_MENU
+1) /* brush */
202 /***********************************************************************
203 * debug_print_menuitem
205 * Print a menuitem in readable form.
208 #define debug_print_menuitem(pre, mp, post) \
209 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
211 #define MENUOUT(text) \
212 TRACE("%s%s", (count++ ? "," : ""), (text))
214 #define MENUFLAG(bit,text) \
216 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 static void do_debug_print_menuitem(const char *prefix
, const MENUITEM
*mp
,
222 static const char * const hbmmenus
[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
223 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
224 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
225 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
226 TRACE("%s ", prefix
);
228 UINT flags
= mp
->fType
;
229 TRACE( "{ ID=0x%lx", mp
->wID
);
231 TRACE( ", Sub=%p", mp
->hSubMenu
);
235 MENUFLAG( MFT_SEPARATOR
, "sep");
236 MENUFLAG( MFT_OWNERDRAW
, "own");
237 MENUFLAG( MFT_BITMAP
, "bit");
238 MENUFLAG(MF_POPUP
, "pop");
239 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
240 MENUFLAG(MFT_MENUBREAK
, "brk");
241 MENUFLAG(MFT_RADIOCHECK
, "radio");
242 MENUFLAG(MFT_RIGHTORDER
, "rorder");
243 MENUFLAG(MF_SYSMENU
, "sys");
244 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
246 TRACE( "+0x%x", flags
);
252 MENUFLAG(MFS_GRAYED
, "grey");
253 MENUFLAG(MFS_DEFAULT
, "default");
254 MENUFLAG(MFS_DISABLED
, "dis");
255 MENUFLAG(MFS_CHECKED
, "check");
256 MENUFLAG(MFS_HILITE
, "hi");
257 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
258 MENUFLAG(MF_MOUSESELECT
, "mouse");
260 TRACE( "+0x%x", flags
);
263 TRACE( ", Chk=%p", mp
->hCheckBit
);
265 TRACE( ", Unc=%p", mp
->hUnCheckBit
);
267 TRACE( ", Text=%s", debugstr_w(mp
->text
));
269 TRACE( ", ItemData=0x%08lx", mp
->dwItemData
);
272 if( IS_MAGIC_BITMAP(mp
->hbmpItem
))
273 TRACE( ", hbitmap=%s", hbmmenus
[ (INT_PTR
)mp
->hbmpItem
+ 1]);
275 TRACE( ", hbitmap=%p", mp
->hbmpItem
);
280 TRACE(" %s\n", postfix
);
287 /***********************************************************************
290 * Validate the given menu handle and returns the menu structure pointer.
292 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
294 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
296 if (menu
== OBJ_OTHER_PROCESS
)
298 WARN( "other process menu %p?\n", hMenu
);
301 if (menu
) release_user_handle_ptr( menu
); /* FIXME! */
302 else WARN("invalid menu handle=%p\n", hMenu
);
306 static POPUPMENU
*grab_menu_ptr(HMENU hMenu
)
308 POPUPMENU
*menu
= get_user_handle_ptr( hMenu
, USER_MENU
);
310 if (menu
== OBJ_OTHER_PROCESS
)
312 WARN("other process menu %p?\n", hMenu
);
319 WARN("invalid menu handle=%p\n", hMenu
);
323 static void release_menu_ptr(POPUPMENU
*menu
)
328 release_user_handle_ptr(menu
);
332 /***********************************************************************
335 * Get the system menu of a window
337 static HMENU
get_win_sys_menu( HWND hwnd
)
340 WND
*win
= WIN_GetPtr( hwnd
);
341 if (win
&& win
!= WND_OTHER_PROCESS
&& win
!= WND_DESKTOP
)
344 WIN_ReleasePtr( win
);
349 /***********************************************************************
352 static HFONT
get_menu_font( BOOL bold
)
354 static HFONT hMenuFont
, hMenuFontBold
;
356 HFONT ret
= bold
? hMenuFontBold
: hMenuFont
;
360 NONCLIENTMETRICSW ncm
;
363 ncm
.cbSize
= sizeof(NONCLIENTMETRICSW
);
364 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSW
), &ncm
, 0);
368 ncm
.lfMenuFont
.lfWeight
+= 300;
369 if (ncm
.lfMenuFont
.lfWeight
> 1000) ncm
.lfMenuFont
.lfWeight
= 1000;
371 if (!(ret
= CreateFontIndirectW( &ncm
.lfMenuFont
))) return 0;
372 prev
= InterlockedCompareExchangePointer( (void **)(bold
? &hMenuFontBold
: &hMenuFont
),
376 /* another thread beat us to it */
384 /***********************************************************************
387 static HBITMAP
get_arrow_bitmap(void)
389 static HBITMAP arrow_bitmap
;
391 if (!arrow_bitmap
) arrow_bitmap
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW
));
395 static inline UINT
get_scroll_arrow_height(const POPUPMENU
*menu
)
397 return menucharsize
.cy
+ 4;
400 /***********************************************************************
403 * Return the default system menu.
405 static HMENU
MENU_CopySysPopup(BOOL mdi
)
407 static const WCHAR sysmenuW
[] = {'S','Y','S','M','E','N','U',0};
408 static const WCHAR sysmenumdiW
[] = {'S','Y','S','M','E','N','U','M','D','I',0};
409 HMENU hMenu
= LoadMenuW(user32_module
, (mdi
? sysmenumdiW
: sysmenuW
));
413 MENUITEMINFOW miteminfo
;
414 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
415 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
416 /* decorate the menu with bitmaps */
417 minfo
.cbSize
= sizeof( MENUINFO
);
418 minfo
.dwStyle
= MNS_CHECKORBMP
;
419 minfo
.fMask
= MIM_STYLE
;
420 SetMenuInfo( hMenu
, &minfo
);
421 miteminfo
.cbSize
= sizeof( MENUITEMINFOW
);
422 miteminfo
.fMask
= MIIM_BITMAP
;
423 miteminfo
.hbmpItem
= HBMMENU_POPUP_CLOSE
;
424 SetMenuItemInfoW( hMenu
, SC_CLOSE
, FALSE
, &miteminfo
);
425 miteminfo
.hbmpItem
= HBMMENU_POPUP_RESTORE
;
426 SetMenuItemInfoW( hMenu
, SC_RESTORE
, FALSE
, &miteminfo
);
427 miteminfo
.hbmpItem
= HBMMENU_POPUP_MAXIMIZE
;
428 SetMenuItemInfoW( hMenu
, SC_MAXIMIZE
, FALSE
, &miteminfo
);
429 miteminfo
.hbmpItem
= HBMMENU_POPUP_MINIMIZE
;
430 SetMenuItemInfoW( hMenu
, SC_MINIMIZE
, FALSE
, &miteminfo
);
431 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
434 ERR("Unable to load default system menu\n" );
436 TRACE("returning %p (mdi=%d).\n", hMenu
, mdi
);
442 /**********************************************************************
445 * Create a copy of the system menu. System menu in Windows is
446 * a special menu bar with the single entry - system menu popup.
447 * This popup is presented to the outside world as a "system menu".
448 * However, the real system menu handle is sometimes seen in the
449 * WM_MENUSELECT parameters (and Word 6 likes it this way).
451 static HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
455 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
456 if ((hMenu
= CreateMenu()))
458 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
459 menu
->wFlags
= MF_SYSMENU
;
460 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
461 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
465 if (GetWindowLongW(hWnd
, GWL_EXSTYLE
) & WS_EX_MDICHILD
)
466 hPopupMenu
= MENU_CopySysPopup(TRUE
);
468 hPopupMenu
= MENU_CopySysPopup(FALSE
);
473 if (GetClassLongW(hWnd
, GCL_STYLE
) & CS_NOCLOSE
)
474 DeleteMenu(hPopupMenu
, SC_CLOSE
, MF_BYCOMMAND
);
476 InsertMenuW( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
477 (UINT_PTR
)hPopupMenu
, NULL
);
479 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
480 menu
->items
[0].fState
= 0;
481 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
483 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
486 DestroyMenu( hMenu
);
488 ERR("failed to load system menu!\n");
493 /***********************************************************************
494 * MENU_InitSysMenuPopup
496 * Grey the appropriate items in System menu.
498 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
502 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
503 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
504 gray
= ((style
& WS_MAXIMIZE
) != 0);
505 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
506 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
507 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
508 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
509 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
510 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
511 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
512 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
514 /* The menu item must keep its state if it's disabled */
516 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
520 /******************************************************************************
522 * UINT MENU_GetStartOfNextColumn(
525 *****************************************************************************/
527 static UINT
MENU_GetStartOfNextColumn(
530 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
534 return NO_SELECTED_ITEM
;
536 i
= menu
->FocusedItem
+ 1;
537 if( i
== NO_SELECTED_ITEM
)
540 for( ; i
< menu
->nItems
; ++i
) {
541 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
545 return NO_SELECTED_ITEM
;
549 /******************************************************************************
551 * UINT MENU_GetStartOfPrevColumn(
554 *****************************************************************************/
556 static UINT
MENU_GetStartOfPrevColumn(
559 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
563 return NO_SELECTED_ITEM
;
565 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
566 return NO_SELECTED_ITEM
;
568 /* Find the start of the column */
570 for(i
= menu
->FocusedItem
; i
!= 0 &&
571 !(menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
));
575 return NO_SELECTED_ITEM
;
577 for(--i
; i
!= 0; --i
) {
578 if (menu
->items
[i
].fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
582 TRACE("ret %d.\n", i
);
587 static POPUPMENU
*find_menu_item(HMENU hmenu
, UINT id
, UINT flags
, UINT
*pos
)
589 UINT fallback_pos
= ~0u, i
;
592 menu
= grab_menu_ptr(hmenu
);
596 if (flags
& MF_BYPOSITION
)
598 if (id
>= menu
->nItems
)
600 release_menu_ptr(menu
);
609 MENUITEM
*item
= menu
->items
;
610 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
612 if (item
->fType
& MF_POPUP
)
614 POPUPMENU
*submenu
= find_menu_item(item
->hSubMenu
, id
, flags
, pos
);
618 release_menu_ptr(menu
);
621 else if (item
->wID
== id
)
623 /* fallback to this item if nothing else found */
627 else if (item
->wID
== id
)
635 if (fallback_pos
!= ~0u)
639 release_menu_ptr(menu
);
646 /***********************************************************************
649 * Find a Sub menu. Return the position of the submenu, and modifies
650 * *hmenu in case it is found in another sub-menu.
651 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
653 static UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
658 if (((*hmenu
)==(HMENU
)0xffff) ||
659 (!(menu
= MENU_GetMenu(*hmenu
))))
660 return NO_SELECTED_ITEM
;
662 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
663 if(!(item
->fType
& MF_POPUP
)) continue;
664 if (item
->hSubMenu
== hSubTarget
) {
668 HMENU hsubmenu
= item
->hSubMenu
;
669 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
670 if (pos
!= NO_SELECTED_ITEM
) {
676 return NO_SELECTED_ITEM
;
679 /***********************************************************************
682 static void MENU_FreeItemData( MENUITEM
* item
)
685 HeapFree( GetProcessHeap(), 0, item
->text
);
688 /***********************************************************************
689 * MENU_AdjustMenuItemRect
691 * Adjust menu item rectangle according to scrolling state.
694 MENU_AdjustMenuItemRect(const POPUPMENU
*menu
, LPRECT rect
)
696 INT scroll_offset
= menu
->bScrolling
? menu
->nScrollPos
: 0;
698 OffsetRect( rect
, menu
->items_rect
.left
, menu
->items_rect
.top
- scroll_offset
);
703 ht_nowhere
, /* outside the menu */
704 ht_border
, /* anywhere that's not an item or a scroll arrow */
705 ht_item
, /* a menu item */
706 ht_scroll_up
, /* scroll up arrow */
707 ht_scroll_down
/* scroll down arrow */
710 /***********************************************************************
711 * MENU_FindItemByCoords
713 * Find the item at the specified coordinates (screen coords). Does
714 * not work for child windows and therefore should not be called for
715 * an arbitrary system menu.
717 * Returns a hittest code. *pos will contain the position of the
718 * item or NO_SELECTED_ITEM. If the hittest code is ht_scroll_up
719 * or ht_scroll_down then *pos will contain the position of the
720 * item that's just outside the items_rect - ie, the one that would
721 * be scrolled completely into view.
723 static enum hittest
MENU_FindItemByCoords( const POPUPMENU
*menu
, POINT pt
, UINT
*pos
)
728 enum hittest ht
= ht_border
;
730 *pos
= NO_SELECTED_ITEM
;
732 if (!GetWindowRect(menu
->hWnd
, &rect
) || !PtInRect(&rect
, pt
))
735 if (GetWindowLongW( menu
->hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) pt
.x
= rect
.right
- 1 - pt
.x
;
736 else pt
.x
-= rect
.left
;
739 if (!PtInRect(&menu
->items_rect
, pt
))
741 if (!menu
->bScrolling
|| pt
.x
< menu
->items_rect
.left
|| pt
.x
>= menu
->items_rect
.right
)
744 /* On a scroll arrow. Update pt so that it points to the item just outside items_rect */
745 if (pt
.y
< menu
->items_rect
.top
)
748 pt
.y
= menu
->items_rect
.top
- 1;
753 pt
.y
= menu
->items_rect
.bottom
;
758 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
761 MENU_AdjustMenuItemRect(menu
, &rect
);
762 if (PtInRect(&rect
, pt
))
765 if (ht
!= ht_scroll_up
&& ht
!= ht_scroll_down
) ht
= ht_item
;
774 /***********************************************************************
777 * Find the menu item selected by a key press.
778 * Return item id, -1 if none, -2 if we should close the menu.
780 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
781 WCHAR key
, BOOL forceMenuChar
)
783 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key
, key
, hmenu
);
785 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
789 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
790 MENUITEM
*item
= menu
->items
;
796 BOOL cjk
= GetSystemMetrics( SM_DBCSENABLED
);
798 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
802 const WCHAR
*p
= item
->text
- 2;
805 const WCHAR
*q
= p
+ 2;
806 p
= strchrW (q
, '&');
807 if (!p
&& cjk
) p
= strchrW (q
, '\036'); /* Japanese Win16 */
809 while (p
!= NULL
&& p
[1] == '&');
810 if (p
&& (toupperW(p
[1]) == toupperW(key
))) return i
;
814 menuchar
= SendMessageW( hwndOwner
, WM_MENUCHAR
,
815 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
816 if (HIWORD(menuchar
) == MNC_EXECUTE
) return LOWORD(menuchar
);
817 if (HIWORD(menuchar
) == MNC_CLOSE
) return (UINT
)(-2);
823 /***********************************************************************
824 * MENU_GetBitmapItemSize
826 * Get the size of a bitmap item.
828 static void MENU_GetBitmapItemSize( MENUITEM
*lpitem
, SIZE
*size
,
832 HBITMAP bmp
= lpitem
->hbmpItem
;
834 size
->cx
= size
->cy
= 0;
836 /* check if there is a magic menu item associated with this item */
837 switch( (INT_PTR
) bmp
)
839 case (INT_PTR
)HBMMENU_CALLBACK
:
841 MEASUREITEMSTRUCT measItem
;
842 measItem
.CtlType
= ODT_MENU
;
844 measItem
.itemID
= lpitem
->wID
;
845 measItem
.itemWidth
= lpitem
->rect
.right
- lpitem
->rect
.left
;
846 measItem
.itemHeight
= lpitem
->rect
.bottom
- lpitem
->rect
.top
;
847 measItem
.itemData
= lpitem
->dwItemData
;
848 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&measItem
);
849 size
->cx
= measItem
.itemWidth
;
850 size
->cy
= measItem
.itemHeight
;
854 case (INT_PTR
)HBMMENU_SYSTEM
:
855 if (lpitem
->dwItemData
)
857 bmp
= (HBITMAP
)lpitem
->dwItemData
;
861 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
862 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
863 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
864 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
865 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
866 size
->cx
= GetSystemMetrics( SM_CYMENU
) - 4;
869 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
870 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
871 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
872 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
873 size
->cx
= GetSystemMetrics( SM_CXMENUSIZE
);
874 size
->cy
= GetSystemMetrics( SM_CYMENUSIZE
);
877 if (GetObjectW(bmp
, sizeof(bm
), &bm
))
879 size
->cx
= bm
.bmWidth
;
880 size
->cy
= bm
.bmHeight
;
884 /***********************************************************************
885 * MENU_DrawBitmapItem
887 * Draw a bitmap item.
889 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
,
890 POPUPMENU
*menu
, HWND hwndOwner
, UINT odaction
)
896 int w
= rect
->right
- rect
->left
;
897 int h
= rect
->bottom
- rect
->top
;
900 HBITMAP hbmToDraw
= lpitem
->hbmpItem
;
903 /* Check if there is a magic menu item associated with this item */
904 if (IS_MAGIC_BITMAP(hbmToDraw
))
910 switch((INT_PTR
)hbmToDraw
)
912 case (INT_PTR
)HBMMENU_SYSTEM
:
913 if (lpitem
->dwItemData
)
915 bmp
= (HBITMAP
)lpitem
->dwItemData
;
916 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
920 static HBITMAP hBmpSysMenu
;
922 if (!hBmpSysMenu
) hBmpSysMenu
= LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE
));
924 if (!GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
925 /* only use right half of the bitmap */
926 bmp_xoffset
= bm
.bmWidth
/ 2;
927 bm
.bmWidth
-= bmp_xoffset
;
930 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
931 flags
= DFCS_CAPTIONRESTORE
;
933 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
934 flags
= DFCS_CAPTIONMIN
;
936 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
937 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
939 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
940 flags
= DFCS_CAPTIONCLOSE
;
942 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
943 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
945 case (INT_PTR
)HBMMENU_CALLBACK
:
947 DRAWITEMSTRUCT drawItem
;
948 drawItem
.CtlType
= ODT_MENU
;
950 drawItem
.itemID
= lpitem
->wID
;
951 drawItem
.itemAction
= odaction
;
952 drawItem
.itemState
= (lpitem
->fState
& MF_CHECKED
)?ODS_CHECKED
:0;
953 drawItem
.itemState
|= (lpitem
->fState
& MF_DEFAULT
)?ODS_DEFAULT
:0;
954 drawItem
.itemState
|= (lpitem
->fState
& MF_DISABLED
)?ODS_DISABLED
:0;
955 drawItem
.itemState
|= (lpitem
->fState
& MF_GRAYED
)?ODS_GRAYED
|ODS_DISABLED
:0;
956 drawItem
.itemState
|= (lpitem
->fState
& MF_HILITE
)?ODS_SELECTED
:0;
957 drawItem
.hwndItem
= (HWND
)menu
->obj
.handle
;
959 drawItem
.itemData
= lpitem
->dwItemData
;
960 drawItem
.rcItem
= *rect
;
961 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&drawItem
);
965 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
968 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
971 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
974 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
978 FIXME("Magic %p not implemented\n", hbmToDraw
);
983 /* draw the magic bitmaps using marlett font characters */
984 /* FIXME: fontsize and the position (x,y) could probably be better */
985 HFONT hfont
, hfontsav
;
986 LOGFONTW logfont
= { 0, 0, 0, 0, FW_NORMAL
,
987 0, 0, 0, SYMBOL_CHARSET
, 0, 0, 0, 0,
988 { 'M','a','r','l','e','t','t',0 } };
989 logfont
.lfHeight
= min( h
, w
) - 5 ;
990 TRACE(" height %d rect %s\n", logfont
.lfHeight
, wine_dbgstr_rect( rect
));
991 hfont
= CreateFontIndirectW( &logfont
);
992 hfontsav
= SelectObject(hdc
, hfont
);
993 TextOutW( hdc
, rect
->left
, rect
->top
+ 2, &bmchr
, 1);
994 SelectObject(hdc
, hfontsav
);
995 DeleteObject( hfont
);
1000 InflateRect( &r
, -1, -1 );
1001 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
1002 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
1007 if (!bmp
|| !GetObjectW( bmp
, sizeof(bm
), &bm
)) return;
1010 hdcMem
= CreateCompatibleDC( hdc
);
1011 SelectObject( hdcMem
, bmp
);
1013 /* handle fontsize > bitmap_height */
1014 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
1016 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_BITMAP(hbmToDraw
)) ? NOTSRCCOPY
: SRCCOPY
;
1017 if ((lpitem
->fState
& MF_HILITE
) && lpitem
->hbmpItem
)
1018 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1019 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
1024 /***********************************************************************
1027 * Calculate the size of the menu item and store it in lpitem->rect.
1029 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
1030 INT orgX
, INT orgY
, BOOL menuBar
, POPUPMENU
* lppop
)
1033 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1034 UINT arrow_bitmap_width
;
1038 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
1039 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
1040 (menuBar
? " (MenuBar)" : ""));
1042 GetObjectW( get_arrow_bitmap(), sizeof(bm
), &bm
);
1043 arrow_bitmap_width
= bm
.bmWidth
;
1045 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1046 if( !menucharsize
.cx
) {
1047 menucharsize
.cx
= GdiGetCharDimensions( hdc
, NULL
, &menucharsize
.cy
);
1048 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1049 * but it is unlikely an application will depend on that */
1050 ODitemheight
= HIWORD( GetDialogBaseUnits());
1053 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
1055 if (lpitem
->fType
& MF_OWNERDRAW
)
1057 MEASUREITEMSTRUCT mis
;
1058 mis
.CtlType
= ODT_MENU
;
1060 mis
.itemID
= lpitem
->wID
;
1061 mis
.itemData
= lpitem
->dwItemData
;
1062 mis
.itemHeight
= ODitemheight
;
1064 SendMessageW( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
1065 /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1066 * width of a menufont character to the width of an owner-drawn menu.
1068 lpitem
->rect
.right
+= mis
.itemWidth
+ 2 * menucharsize
.cx
;
1070 /* under at least win95 you seem to be given a standard
1071 height for the menu and the height value is ignored */
1072 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENUSIZE
);
1074 lpitem
->rect
.bottom
+= mis
.itemHeight
;
1076 TRACE("id=%04lx size=%dx%d\n",
1077 lpitem
->wID
, lpitem
->rect
.right
-lpitem
->rect
.left
,
1078 lpitem
->rect
.bottom
-lpitem
->rect
.top
);
1082 if (lpitem
->fType
& MF_SEPARATOR
)
1084 lpitem
->rect
.bottom
+= GetSystemMetrics( SM_CYMENUSIZE
)/2;
1086 lpitem
->rect
.right
+= arrow_bitmap_width
+ menucharsize
.cx
;
1094 if (lpitem
->hbmpItem
) {
1097 MENU_GetBitmapItemSize(lpitem
, &size
, hwndOwner
);
1098 /* Keep the size of the bitmap in callback mode to be able
1099 * to draw it correctly */
1100 lpitem
->bmpsize
= size
;
1101 lppop
->textOffset
= max( lppop
->textOffset
, size
.cx
);
1102 lpitem
->rect
.right
+= size
.cx
+ 2;
1103 itemheight
= size
.cy
+ 2;
1105 if( !(lppop
->dwStyle
& MNS_NOCHECK
))
1106 lpitem
->rect
.right
+= check_bitmap_width
;
1107 lpitem
->rect
.right
+= 4 + menucharsize
.cx
;
1108 lpitem
->xTab
= lpitem
->rect
.right
;
1109 lpitem
->rect
.right
+= arrow_bitmap_width
;
1110 } else if (lpitem
->hbmpItem
) { /* menuBar */
1113 MENU_GetBitmapItemSize( lpitem
, &size
, hwndOwner
);
1114 lpitem
->bmpsize
= size
;
1115 lpitem
->rect
.right
+= size
.cx
;
1116 if( lpitem
->text
) lpitem
->rect
.right
+= 2;
1117 itemheight
= size
.cy
;
1120 /* it must be a text item - unless it's the system menu */
1121 if (!(lpitem
->fType
& MF_SYSMENU
) && lpitem
->text
) {
1122 HFONT hfontOld
= NULL
;
1123 RECT rc
= lpitem
->rect
;
1124 LONG txtheight
, txtwidth
;
1126 if ( lpitem
->fState
& MFS_DEFAULT
) {
1127 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1130 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1131 DT_SINGLELINE
|DT_CALCRECT
);
1132 lpitem
->rect
.right
+= rc
.right
- rc
.left
;
1133 itemheight
= max( max( itemheight
, txtheight
),
1134 GetSystemMetrics( SM_CYMENU
) - 1);
1135 lpitem
->rect
.right
+= 2 * menucharsize
.cx
;
1137 if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
) {
1140 int n
= (int)( p
- lpitem
->text
);
1141 /* Item contains a tab (only meaningful in popup menus) */
1142 /* get text size before the tab */
1143 txtheight
= DrawTextW( hdc
, lpitem
->text
, n
, &rc
,
1144 DT_SINGLELINE
|DT_CALCRECT
);
1145 txtwidth
= rc
.right
- rc
.left
;
1146 p
+= 1; /* advance past the Tab */
1147 /* get text size after the tab */
1148 tmpheight
= DrawTextW( hdc
, p
, -1, &tmprc
,
1149 DT_SINGLELINE
|DT_CALCRECT
);
1150 lpitem
->xTab
+= txtwidth
;
1151 txtheight
= max( txtheight
, tmpheight
);
1152 txtwidth
+= menucharsize
.cx
+ /* space for the tab */
1153 tmprc
.right
- tmprc
.left
; /* space for the short cut */
1155 txtheight
= DrawTextW( hdc
, lpitem
->text
, -1, &rc
,
1156 DT_SINGLELINE
|DT_CALCRECT
);
1157 txtwidth
= rc
.right
- rc
.left
;
1158 lpitem
->xTab
+= txtwidth
;
1160 lpitem
->rect
.right
+= 2 + txtwidth
;
1161 itemheight
= max( itemheight
,
1162 max( txtheight
+ 2, menucharsize
.cy
+ 4));
1164 if (hfontOld
) SelectObject (hdc
, hfontOld
);
1165 } else if( menuBar
) {
1166 itemheight
= max( itemheight
, GetSystemMetrics(SM_CYMENU
)-1);
1168 lpitem
->rect
.bottom
+= itemheight
;
1169 TRACE("%s\n", wine_dbgstr_rect( &lpitem
->rect
));
1172 /***********************************************************************
1173 * MENU_PopupMenuCalcSize
1175 * Calculate the size of a popup menu.
1177 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, UINT max_height
)
1182 BOOL textandbmp
= FALSE
, multi_col
= FALSE
;
1183 int orgX
, orgY
, maxTab
, maxTabWidth
;
1185 lppop
->Width
= lppop
->Height
= 0;
1186 SetRectEmpty(&lppop
->items_rect
);
1188 if (lppop
->nItems
== 0) return;
1191 SelectObject( hdc
, get_menu_font(FALSE
));
1195 lppop
->textOffset
= 0;
1197 while (start
< lppop
->nItems
)
1199 lpitem
= &lppop
->items
[start
];
1200 orgX
= lppop
->items_rect
.right
;
1201 if( lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1202 orgX
+= MENU_COL_SPACE
;
1203 orgY
= lppop
->items_rect
.top
;
1205 maxTab
= maxTabWidth
= 0;
1206 /* Parse items until column break or end of menu */
1207 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1209 if (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))
1212 if (i
!= start
) break;
1215 MENU_CalcItemSize( hdc
, lpitem
, lppop
->hwndOwner
, orgX
, orgY
, FALSE
, lppop
);
1216 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, lpitem
->rect
.right
);
1217 orgY
= lpitem
->rect
.bottom
;
1218 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1220 maxTab
= max( maxTab
, lpitem
->xTab
);
1221 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1223 if( lpitem
->text
&& lpitem
->hbmpItem
) textandbmp
= TRUE
;
1226 /* Finish the column (set all items to the largest width found) */
1227 lppop
->items_rect
.right
= max( lppop
->items_rect
.right
, maxTab
+ maxTabWidth
);
1228 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1230 lpitem
->rect
.right
= lppop
->items_rect
.right
;
1231 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1232 lpitem
->xTab
= maxTab
;
1235 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, orgY
);
1238 /* if none of the items have both text and bitmap then
1239 * the text and bitmaps are all aligned on the left. If there is at
1240 * least one item with both text and bitmap then bitmaps are
1241 * on the left and texts left aligned with the right hand side
1243 if( !textandbmp
) lppop
->textOffset
= 0;
1245 lppop
->nTotalHeight
= lppop
->items_rect
.bottom
;
1247 /* space for the border */
1248 OffsetRect(&lppop
->items_rect
, MENU_MARGIN
, MENU_MARGIN
);
1249 lppop
->Height
= lppop
->items_rect
.bottom
+ MENU_MARGIN
;
1250 lppop
->Width
= lppop
->items_rect
.right
+ MENU_MARGIN
;
1252 /* Adjust popup height if it exceeds maximum */
1253 if (lppop
->Height
>= max_height
)
1255 lppop
->Height
= max_height
;
1256 lppop
->bScrolling
= !multi_col
;
1257 /* When the scroll arrows are present, don't add the top/bottom margin as well */
1258 if (lppop
->bScrolling
)
1260 lppop
->items_rect
.top
= get_scroll_arrow_height(lppop
);
1261 lppop
->items_rect
.bottom
= lppop
->Height
- get_scroll_arrow_height(lppop
);
1266 lppop
->bScrolling
= FALSE
;
1269 ReleaseDC( 0, hdc
);
1273 /***********************************************************************
1274 * MENU_MenuBarCalcSize
1276 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1277 * height is off by 1 pixel which causes lengthy window relocations when
1278 * active document window is maximized/restored.
1280 * Calculate the size of the menu bar.
1282 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1283 LPPOPUPMENU lppop
, HWND hwndOwner
)
1286 UINT start
, i
, helpPos
;
1289 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1290 if (lppop
->nItems
== 0) return;
1291 TRACE("lprect %p %s\n", lprect
, wine_dbgstr_rect( lprect
));
1292 /* Start with a 1 pixel top border.
1293 This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
1294 SetRect(&lppop
->items_rect
, 0, 0, lprect
->right
- lprect
->left
, 1);
1297 lppop
->textOffset
= 0;
1298 while (start
< lppop
->nItems
)
1300 lpitem
= &lppop
->items
[start
];
1301 orgX
= lppop
->items_rect
.left
;
1302 orgY
= lppop
->items_rect
.bottom
;
1304 /* Parse items until line break or end of menu */
1305 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1307 if ((helpPos
== ~0U) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1309 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1311 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX
, orgY
);
1312 debug_print_menuitem (" item: ", lpitem
, "");
1313 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
, lppop
);
1315 if (lpitem
->rect
.right
> lppop
->items_rect
.right
)
1317 if (i
!= start
) break;
1318 else lpitem
->rect
.right
= lppop
->items_rect
.right
;
1320 lppop
->items_rect
.bottom
= max( lppop
->items_rect
.bottom
, lpitem
->rect
.bottom
);
1321 orgX
= lpitem
->rect
.right
;
1324 /* Finish the line (set all items to the largest height found) */
1325 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= lppop
->items_rect
.bottom
;
1328 OffsetRect(&lppop
->items_rect
, lprect
->left
, lprect
->top
);
1329 lppop
->Width
= lppop
->items_rect
.right
- lppop
->items_rect
.left
;
1330 lppop
->Height
= lppop
->items_rect
.bottom
- lppop
->items_rect
.top
;
1331 lprect
->bottom
= lppop
->items_rect
.bottom
;
1333 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1334 /* the last item (if several lines, only move the last line) */
1335 if (helpPos
== ~0U) return;
1336 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1337 orgY
= lpitem
->rect
.top
;
1338 orgX
= lprect
->right
- lprect
->left
;
1339 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1340 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1341 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1342 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1343 lpitem
->rect
.right
= orgX
;
1344 orgX
= lpitem
->rect
.left
;
1348 static void draw_scroll_arrow(HDC hdc
, int x
, int top
, int height
, BOOL up
, BOOL enabled
)
1350 RECT rect
, light_rect
;
1351 HBRUSH brush
= GetSysColorBrush( enabled
? COLOR_BTNTEXT
: COLOR_BTNSHADOW
);
1352 HBRUSH light
= GetSysColorBrush( COLOR_3DLIGHT
);
1359 SetRect( &rect
, x
+ 1, top
, x
+ 2, top
+ 1);
1360 FillRect( hdc
, &rect
, light
);
1365 SetRect( &rect
, x
, top
, x
+ 1, top
+ 1);
1368 FillRect( hdc
, &rect
, brush
);
1369 if (!enabled
&& !up
&& height
)
1371 SetRect( &light_rect
, rect
.right
, rect
.top
, rect
.right
+ 2, rect
.bottom
);
1372 FillRect( hdc
, &light_rect
, light
);
1374 InflateRect( &rect
, 1, 0 );
1375 OffsetRect( &rect
, 0, up
? 1 : -1 );
1381 FillRect( hdc
, &rect
, light
);
1385 /***********************************************************************
1386 * MENU_DrawScrollArrows
1388 * Draw scroll arrows.
1391 MENU_DrawScrollArrows(const POPUPMENU
*menu
, HDC hdc
)
1393 UINT full_height
= get_scroll_arrow_height( menu
);
1394 UINT arrow_height
= full_height
/ 3;
1395 BOOL at_end
= menu
->nScrollPos
+ menu
->items_rect
.bottom
- menu
->items_rect
.top
== menu
->nTotalHeight
;
1397 draw_scroll_arrow( hdc
, menu
->Width
/ 3, arrow_height
, arrow_height
,
1398 TRUE
, menu
->nScrollPos
!= 0);
1399 draw_scroll_arrow( hdc
, menu
->Width
/ 3, menu
->Height
- 2 * arrow_height
, arrow_height
,
1404 /***********************************************************************
1407 * Draws the popup-menu arrow.
1409 static void draw_popup_arrow( HDC hdc
, RECT rect
, UINT arrow_bitmap_width
,
1410 UINT arrow_bitmap_height
)
1412 HDC hdcMem
= CreateCompatibleDC( hdc
);
1413 HBITMAP hOrigBitmap
;
1415 hOrigBitmap
= SelectObject( hdcMem
, get_arrow_bitmap() );
1416 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1417 (rect
.top
+ rect
.bottom
- arrow_bitmap_height
) / 2,
1418 arrow_bitmap_width
, arrow_bitmap_height
,
1419 hdcMem
, 0, 0, SRCCOPY
);
1420 SelectObject( hdcMem
, hOrigBitmap
);
1423 /***********************************************************************
1426 * Draw a single menu item.
1428 static void MENU_DrawMenuItem( HWND hwnd
, POPUPMENU
*menu
, HWND hwndOwner
, HDC hdc
,
1429 MENUITEM
*lpitem
, BOOL menuBar
, UINT odaction
)
1432 BOOL flat_menu
= FALSE
;
1434 UINT arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
1435 HRGN old_clip
= NULL
, clip
;
1437 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1441 GetObjectW( get_arrow_bitmap(), sizeof(bmp
), &bmp
);
1442 arrow_bitmap_width
= bmp
.bmWidth
;
1443 arrow_bitmap_height
= bmp
.bmHeight
;
1446 if (lpitem
->fType
& MF_SYSMENU
)
1448 if( !IsIconic(hwnd
) )
1449 NC_DrawSysButton( hwnd
, hdc
, lpitem
->fState
& (MF_HILITE
| MF_MOUSESELECT
) );
1453 TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem
->rect
) );
1454 rect
= lpitem
->rect
;
1455 MENU_AdjustMenuItemRect( menu
, &rect
);
1456 if (!IntersectRect( &bmprc
, &rect
, &menu
->items_rect
)) /* bmprc is used as a dummy */
1459 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1460 bkgnd
= (menuBar
&& flat_menu
) ? COLOR_MENUBAR
: COLOR_MENU
;
1464 if (lpitem
->fState
& MF_HILITE
)
1466 if(menuBar
&& !flat_menu
) {
1467 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1468 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1470 if(lpitem
->fState
& MF_GRAYED
)
1471 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1473 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1474 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1479 if (lpitem
->fState
& MF_GRAYED
)
1480 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1482 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1483 SetBkColor( hdc
, GetSysColor( bkgnd
) );
1486 old_clip
= CreateRectRgn( 0, 0, 0, 0 );
1487 if (GetClipRgn( hdc
, old_clip
) <= 0)
1489 DeleteObject( old_clip
);
1492 clip
= CreateRectRgnIndirect( &menu
->items_rect
);
1493 ExtSelectClipRgn( hdc
, clip
, RGN_AND
);
1494 DeleteObject( clip
);
1496 if (lpitem
->fType
& MF_OWNERDRAW
)
1499 ** Experimentation under Windows reveals that an owner-drawn
1500 ** menu is given the rectangle which includes the space it requested
1501 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1502 ** and a popup-menu arrow. This is the value of lpitem->rect.
1503 ** Windows will leave all drawing to the application except for
1504 ** the popup-menu arrow. Windows always draws that itself, after
1505 ** the menu owner has finished drawing.
1508 COLORREF old_bk
, old_text
;
1510 dis
.CtlType
= ODT_MENU
;
1512 dis
.itemID
= lpitem
->wID
;
1513 dis
.itemData
= lpitem
->dwItemData
;
1515 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1516 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1517 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1518 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1519 dis
.hwndItem
= (HWND
)menu
->obj
.handle
;
1522 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1523 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner
,
1524 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1525 dis
.hDC
, wine_dbgstr_rect( &dis
.rcItem
));
1526 old_bk
= GetBkColor( hdc
);
1527 old_text
= GetTextColor( hdc
);
1528 SendMessageW( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1529 /* Draw the popup-menu arrow */
1530 SetBkColor( hdc
, old_bk
);
1531 SetTextColor( hdc
, old_text
);
1532 if (lpitem
->fType
& MF_POPUP
)
1533 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1534 arrow_bitmap_height
);
1538 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) goto done
;
1540 if (lpitem
->fState
& MF_HILITE
)
1544 InflateRect (&rect
, -1, -1);
1545 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_MENUHILIGHT
));
1546 InflateRect (&rect
, 1, 1);
1547 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1552 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1554 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1558 FillRect( hdc
, &rect
, GetSysColorBrush(bkgnd
) );
1560 SetBkMode( hdc
, TRANSPARENT
);
1562 /* vertical separator */
1563 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1568 rc
.left
-= MENU_COL_SPACE
/ 2 + 1;
1570 rc
.bottom
= menu
->Height
- 3;
1573 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1574 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1575 LineTo( hdc
, rc
.left
, rc
.bottom
);
1576 SelectObject( hdc
, oldPen
);
1579 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1582 /* horizontal separator */
1583 if (lpitem
->fType
& MF_SEPARATOR
)
1588 InflateRect( &rc
, -1, 0 );
1589 rc
.top
= ( rc
.top
+ rc
.bottom
) / 2;
1592 oldPen
= SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_BTNSHADOW
) );
1593 MoveToEx( hdc
, rc
.left
, rc
.top
, NULL
);
1594 LineTo( hdc
, rc
.right
, rc
.top
);
1595 SelectObject( hdc
, oldPen
);
1598 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1602 /* helper lines for debugging */
1603 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1604 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1605 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1606 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1609 if (lpitem
->hbmpItem
) {
1610 /* calculate the bitmap rectangle in coordinates relative
1611 * to the item rectangle */
1613 if( lpitem
->hbmpItem
== HBMMENU_CALLBACK
)
1616 bmprc
.left
= lpitem
->text
? menucharsize
.cx
: 0;
1618 else if (menu
->dwStyle
& MNS_NOCHECK
)
1620 else if (menu
->dwStyle
& MNS_CHECKORBMP
)
1623 bmprc
.left
= 4 + GetSystemMetrics(SM_CXMENUCHECK
);
1624 bmprc
.right
= bmprc
.left
+ lpitem
->bmpsize
.cx
;
1625 if( menuBar
&& !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1628 bmprc
.top
= (rect
.bottom
- rect
.top
-
1629 lpitem
->bmpsize
.cy
) / 2;
1630 bmprc
.bottom
= bmprc
.top
+ lpitem
->bmpsize
.cy
;
1636 INT y
= rect
.top
+ rect
.bottom
;
1637 BOOL checked
= FALSE
;
1638 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1639 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1640 /* Draw the check mark
1643 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1645 if (!(menu
->dwStyle
& MNS_NOCHECK
))
1647 bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
:
1648 lpitem
->hUnCheckBit
;
1649 if (bm
) /* we have a custom bitmap */
1651 HDC hdcMem
= CreateCompatibleDC( hdc
);
1653 SelectObject( hdcMem
, bm
);
1654 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1655 check_bitmap_width
, check_bitmap_height
,
1656 hdcMem
, 0, 0, SRCCOPY
);
1660 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1663 HBITMAP bm
= CreateBitmap( check_bitmap_width
,
1664 check_bitmap_height
, 1, 1, NULL
);
1665 HDC hdcMem
= CreateCompatibleDC( hdc
);
1667 SelectObject( hdcMem
, bm
);
1668 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1669 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1670 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1671 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1672 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1673 hdcMem
, 0, 0, SRCCOPY
);
1679 if (lpitem
->hbmpItem
&& !(checked
&& (menu
->dwStyle
& MNS_CHECKORBMP
)))
1682 /* some applications make this assumption on the DC's origin */
1683 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1684 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1685 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1687 /* Draw the popup-menu arrow */
1688 if (lpitem
->fType
& MF_POPUP
)
1689 draw_popup_arrow( hdc
, rect
, arrow_bitmap_width
,
1690 arrow_bitmap_height
);
1692 if( !(menu
->dwStyle
& MNS_NOCHECK
))
1693 rect
.left
+= check_bitmap_width
;
1694 rect
.right
-= arrow_bitmap_width
;
1696 else if (lpitem
->hbmpItem
)
1697 { /* Draw the bitmap */
1700 SetViewportOrgEx( hdc
, rect
.left
, rect
.top
, &origorg
);
1701 MENU_DrawBitmapItem( hdc
, lpitem
, &bmprc
, menu
, hwndOwner
, odaction
);
1702 SetViewportOrgEx( hdc
, origorg
.x
, origorg
.y
, NULL
);
1704 /* process text if present */
1710 UINT uFormat
= (menuBar
) ?
1711 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1712 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1714 if( !(menu
->dwStyle
& MNS_CHECKORBMP
))
1715 rect
.left
+= menu
->textOffset
;
1717 if ( lpitem
->fState
& MFS_DEFAULT
)
1719 hfontOld
= SelectObject( hdc
, get_menu_font(TRUE
) );
1723 if( lpitem
->hbmpItem
)
1724 rect
.left
+= lpitem
->bmpsize
.cx
;
1725 if( !(lpitem
->hbmpItem
== HBMMENU_CALLBACK
))
1726 rect
.left
+= menucharsize
.cx
;
1727 rect
.right
-= menucharsize
.cx
;
1730 for (i
= 0; lpitem
->text
[i
]; i
++)
1731 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1734 if(lpitem
->fState
& MF_GRAYED
)
1736 if (!(lpitem
->fState
& MF_HILITE
) )
1738 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1739 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1740 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1741 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1743 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1746 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1748 /* paint the shortcut text */
1749 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1751 if (lpitem
->text
[i
] == '\t')
1753 rect
.left
= lpitem
->xTab
;
1754 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1758 rect
.right
= lpitem
->xTab
;
1759 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1762 if(lpitem
->fState
& MF_GRAYED
)
1764 if (!(lpitem
->fState
& MF_HILITE
) )
1766 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1767 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1768 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1769 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1771 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1773 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1777 SelectObject (hdc
, hfontOld
);
1781 ExtSelectClipRgn( hdc
, old_clip
, RGN_COPY
);
1782 if (old_clip
) DeleteObject( old_clip
);
1786 /***********************************************************************
1787 * MENU_DrawPopupMenu
1789 * Paint a popup menu.
1791 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1793 HBRUSH hPrevBrush
, brush
= GetSysColorBrush( COLOR_MENU
);
1795 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1797 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1799 GetClientRect( hwnd
, &rect
);
1801 if (menu
&& menu
->hbrBack
) brush
= menu
->hbrBack
;
1802 if ((hPrevBrush
= SelectObject( hdc
, brush
))
1803 && SelectObject( hdc
, get_menu_font(FALSE
) ))
1807 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1809 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1812 BOOL flat_menu
= FALSE
;
1814 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
1816 FrameRect(hdc
, &rect
, GetSysColorBrush(COLOR_BTNSHADOW
));
1818 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1822 TRACE("hmenu %p Style %08x\n", hmenu
, menu
->dwStyle
);
1823 /* draw menu items */
1830 for (u
= menu
->nItems
; u
> 0; u
--, item
++)
1831 MENU_DrawMenuItem( hwnd
, menu
, menu
->hwndOwner
, hdc
,
1832 item
, FALSE
, ODA_DRAWENTIRE
);
1834 /* draw scroll arrows */
1835 if (menu
->bScrolling
)
1836 MENU_DrawScrollArrows(menu
, hdc
);
1840 SelectObject( hdc
, hPrevBrush
);
1845 /***********************************************************************
1848 * Paint a menu bar. Returns the height of the menu bar.
1849 * called from [windows/nonclient.c]
1851 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
)
1854 HMENU hMenu
= GetMenu(hwnd
);
1856 lppop
= MENU_GetMenu( hMenu
);
1857 if (lppop
== NULL
|| lprect
== NULL
)
1859 return GetSystemMetrics(SM_CYMENU
);
1862 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1866 /***********************************************************************
1869 * Popup menu initialization before WM_ENTERMENULOOP.
1871 static BOOL
MENU_InitPopup( HWND hwndOwner
, HMENU hmenu
, UINT flags
)
1876 TRACE("owner=%p hmenu=%p\n", hwndOwner
, hmenu
);
1878 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1880 /* store the owner for DrawItem */
1881 if (!IsWindow( hwndOwner
))
1883 SetLastError( ERROR_INVALID_WINDOW_HANDLE
);
1886 menu
->hwndOwner
= hwndOwner
;
1888 if (flags
& TPM_LAYOUTRTL
)
1889 ex_style
= WS_EX_LAYOUTRTL
;
1891 /* NOTE: In Windows, top menu popup is not owned. */
1892 menu
->hWnd
= CreateWindowExW( ex_style
, (LPCWSTR
)POPUPMENU_CLASS_ATOM
, NULL
,
1893 WS_POPUP
, 0, 0, 0, 0,
1894 hwndOwner
, 0, (HINSTANCE
)GetWindowLongPtrW(hwndOwner
, GWLP_HINSTANCE
),
1896 if( !menu
->hWnd
) return FALSE
;
1901 /***********************************************************************
1904 * Display a popup menu.
1906 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
, UINT flags
,
1907 INT x
, INT y
, INT xanchor
, INT yanchor
)
1915 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1916 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1918 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1919 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1921 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1922 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1925 menu
->nScrollPos
= 0;
1927 /* FIXME: should use item rect */
1930 monitor
= MonitorFromPoint( pt
, MONITOR_DEFAULTTONEAREST
);
1931 info
.cbSize
= sizeof(info
);
1932 GetMonitorInfoW( monitor
, &info
);
1934 max_height
= info
.rcWork
.bottom
- info
.rcWork
.top
;
1936 max_height
= min( max_height
, menu
->cyMax
);
1938 MENU_PopupMenuCalcSize( menu
, max_height
);
1940 /* adjust popup menu pos so that it fits within the desktop */
1942 if (flags
& TPM_LAYOUTRTL
)
1943 flags
^= TPM_RIGHTALIGN
;
1945 if( flags
& TPM_RIGHTALIGN
) x
-= menu
->Width
;
1946 if( flags
& TPM_CENTERALIGN
) x
-= menu
->Width
/ 2;
1948 if( flags
& TPM_BOTTOMALIGN
) y
-= menu
->Height
;
1949 if( flags
& TPM_VCENTERALIGN
) y
-= menu
->Height
/ 2;
1951 if( x
+ menu
->Width
> info
.rcWork
.right
)
1953 if( xanchor
&& x
>= menu
->Width
- xanchor
)
1954 x
-= menu
->Width
- xanchor
;
1956 if( x
+ menu
->Width
> info
.rcWork
.right
)
1957 x
= info
.rcWork
.right
- menu
->Width
;
1959 if( x
< info
.rcWork
.left
) x
= info
.rcWork
.left
;
1961 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1963 if( yanchor
&& y
>= menu
->Height
+ yanchor
)
1964 y
-= menu
->Height
+ yanchor
;
1966 if( y
+ menu
->Height
> info
.rcWork
.bottom
)
1967 y
= info
.rcWork
.bottom
- menu
->Height
;
1969 if( y
< info
.rcWork
.top
) y
= info
.rcWork
.top
;
1972 top_popup
= menu
->hWnd
;
1973 top_popup_hmenu
= hmenu
;
1975 /* Display the window */
1977 SetWindowPos( menu
->hWnd
, HWND_TOPMOST
, x
, y
, menu
->Width
, menu
->Height
,
1978 SWP_SHOWWINDOW
| SWP_NOACTIVATE
);
1979 UpdateWindow( menu
->hWnd
);
1984 /***********************************************************************
1985 * MENU_EnsureMenuItemVisible
1988 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop
, UINT wIndex
, HDC hdc
)
1990 if (lppop
->bScrolling
)
1992 MENUITEM
*item
= &lppop
->items
[wIndex
];
1993 UINT nOldPos
= lppop
->nScrollPos
;
1994 const RECT
*rc
= &lppop
->items_rect
;
1995 UINT scroll_height
= rc
->bottom
- rc
->top
;
1997 if (item
->rect
.bottom
> lppop
->nScrollPos
+ scroll_height
)
1999 lppop
->nScrollPos
= item
->rect
.bottom
- scroll_height
;
2000 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
2002 else if (item
->rect
.top
< lppop
->nScrollPos
)
2004 lppop
->nScrollPos
= item
->rect
.top
;
2005 ScrollWindow(lppop
->hWnd
, 0, nOldPos
- lppop
->nScrollPos
, rc
, rc
);
2008 /* Invalidate the scroll arrows if necessary */
2009 if (nOldPos
!= lppop
->nScrollPos
)
2011 RECT arrow_rect
= lppop
->items_rect
;
2012 if (nOldPos
== 0 || lppop
->nScrollPos
== 0)
2015 arrow_rect
.bottom
= lppop
->items_rect
.top
;
2016 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
2018 if (nOldPos
+ scroll_height
== lppop
->nTotalHeight
||
2019 lppop
->nScrollPos
+ scroll_height
== lppop
->nTotalHeight
)
2021 arrow_rect
.top
= lppop
->items_rect
.bottom
;
2022 arrow_rect
.bottom
= lppop
->Height
;
2023 InvalidateRect(lppop
->hWnd
, &arrow_rect
, FALSE
);
2030 /***********************************************************************
2033 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
2034 BOOL sendMenuSelect
, HMENU topmenu
)
2039 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
2041 lppop
= MENU_GetMenu( hmenu
);
2042 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
2044 if (lppop
->FocusedItem
== wIndex
) return;
2045 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
2046 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2048 top_popup
= lppop
->hWnd
;
2049 top_popup_hmenu
= hmenu
;
2052 SelectObject( hdc
, get_menu_font(FALSE
));
2054 /* Clear previous highlighted item */
2055 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2057 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
2058 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[lppop
->FocusedItem
],
2059 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2062 /* Highlight new item (if any) */
2063 lppop
->FocusedItem
= wIndex
;
2064 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
2066 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
2067 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
2068 MENU_EnsureMenuItemVisible(lppop
, wIndex
, hdc
);
2069 MENU_DrawMenuItem( lppop
->hWnd
, lppop
, hwndOwner
, hdc
, &lppop
->items
[wIndex
],
2070 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
2074 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
2075 SendMessageW( hwndOwner
, WM_MENUSELECT
,
2076 MAKEWPARAM(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
2077 ip
->fType
| ip
->fState
|
2078 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
2081 else if (sendMenuSelect
) {
2084 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
2085 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
2086 MENUITEM
*ip
= &ptm
->items
[pos
];
2087 SendMessageW( hwndOwner
, WM_MENUSELECT
, MAKEWPARAM(pos
,
2088 ip
->fType
| ip
->fState
|
2089 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
2093 ReleaseDC( lppop
->hWnd
, hdc
);
2097 /***********************************************************************
2098 * MENU_MoveSelection
2100 * Moves currently selected item according to the offset parameter.
2101 * If there is no selection then it should select the last item if
2102 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2104 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
2109 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
2111 menu
= MENU_GetMenu( hmenu
);
2112 if ((!menu
) || (!menu
->items
)) return;
2114 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2116 if( menu
->nItems
== 1 ) return; else
2117 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
2119 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2121 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2126 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
2127 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
2128 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
2130 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
2136 /**********************************************************************
2139 * Insert (allocate) a new item into a menu.
2141 static POPUPMENU
*insert_menu_item(HMENU hMenu
, UINT id
, UINT flags
, UINT
*ret_pos
)
2147 /* Find where to insert new item */
2148 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
2150 if (!(menu
= grab_menu_ptr(hMenu
)))
2155 /* Make sure that MDI system buttons stay on the right side.
2156 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2157 * regardless of their id.
2159 while (pos
> 0 && (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
>= (INT_PTR
)HBMMENU_SYSTEM
&&
2160 (INT_PTR
)menu
->items
[pos
- 1].hbmpItem
<= (INT_PTR
)HBMMENU_MBAR_CLOSE_D
)
2163 TRACE("inserting at %u flags %x\n", pos
, flags
);
2165 /* Create new items array */
2167 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
2170 release_menu_ptr(menu
);
2171 WARN("allocation failed\n" );
2174 if (menu
->nItems
> 0)
2176 /* Copy the old array into the new one */
2177 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
2178 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
2179 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
2180 HeapFree( GetProcessHeap(), 0, menu
->items
);
2182 menu
->items
= newItems
;
2184 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
2185 menu
->Height
= 0; /* force size recalculate */
2192 /**********************************************************************
2193 * MENU_ParseResource
2195 * Parse a standard menu resource and add items to the menu.
2196 * Return a pointer to the end of the resource.
2198 * NOTE: flags is equivalent to the mtOption field
2200 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
)
2208 flags
= GET_WORD(res
);
2209 end_flag
= flags
& MF_END
;
2210 /* Remove MF_END because it has the same value as MF_HILITE */
2212 res
+= sizeof(WORD
);
2213 if (!(flags
& MF_POPUP
))
2216 res
+= sizeof(WORD
);
2219 res
+= (strlenW(str
) + 1) * sizeof(WCHAR
);
2220 if (flags
& MF_POPUP
)
2222 HMENU hSubMenu
= CreatePopupMenu();
2223 if (!hSubMenu
) return NULL
;
2224 if (!(res
= MENU_ParseResource( res
, hSubMenu
))) return NULL
;
2225 AppendMenuW( hMenu
, flags
, (UINT_PTR
)hSubMenu
, str
);
2227 else /* Not a popup */
2229 AppendMenuW( hMenu
, flags
, id
, *str
? str
: NULL
);
2231 } while (!end_flag
);
2236 /**********************************************************************
2237 * MENUEX_ParseResource
2239 * Parse an extended menu resource and add items to the menu.
2240 * Return a pointer to the end of the resource.
2242 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
2248 mii
.cbSize
= sizeof(mii
);
2249 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
2250 mii
.fType
= GET_DWORD(res
);
2251 res
+= sizeof(DWORD
);
2252 mii
.fState
= GET_DWORD(res
);
2253 res
+= sizeof(DWORD
);
2254 mii
.wID
= GET_DWORD(res
);
2255 res
+= sizeof(DWORD
);
2256 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
2257 res
+= sizeof(WORD
);
2258 /* Align the text on a word boundary. */
2259 res
+= (~((UINT_PTR
)res
- 1)) & 1;
2260 mii
.dwTypeData
= (LPWSTR
) res
;
2261 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
2262 /* Align the following fields on a dword boundary. */
2263 res
+= (~((UINT_PTR
)res
- 1)) & 3;
2265 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2266 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
2268 if (resinfo
& 1) { /* Pop-up? */
2269 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2270 res
+= sizeof(DWORD
);
2271 mii
.hSubMenu
= CreatePopupMenu();
2274 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
2275 DestroyMenu(mii
.hSubMenu
);
2278 mii
.fMask
|= MIIM_SUBMENU
;
2279 mii
.fType
|= MF_POPUP
;
2281 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
2283 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2284 mii
.wID
, mii
.fType
);
2285 mii
.fType
|= MF_SEPARATOR
;
2287 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
2288 } while (!(resinfo
& MF_END
));
2293 /***********************************************************************
2296 * Return the handle of the selected sub-popup menu (if any).
2298 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
2303 menu
= MENU_GetMenu( hmenu
);
2305 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
2307 item
= &menu
->items
[menu
->FocusedItem
];
2308 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
2309 return item
->hSubMenu
;
2314 /***********************************************************************
2315 * MENU_HideSubPopups
2317 * Hide the sub-popup menus of this menu.
2319 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
2320 BOOL sendMenuSelect
, UINT wFlags
)
2322 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
2324 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
2326 if (menu
&& top_popup
)
2332 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2334 item
= &menu
->items
[menu
->FocusedItem
];
2335 if (!(item
->fType
& MF_POPUP
) ||
2336 !(item
->fState
& MF_MOUSESELECT
)) return;
2337 item
->fState
&= ~MF_MOUSESELECT
;
2338 hsubmenu
= item
->hSubMenu
;
2341 if (!(submenu
= MENU_GetMenu( hsubmenu
))) return;
2342 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
, wFlags
);
2343 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2344 DestroyWindow( submenu
->hWnd
);
2347 if (!(wFlags
& TPM_NONOTIFY
))
2348 SendMessageW( hwndOwner
, WM_UNINITMENUPOPUP
, (WPARAM
)hsubmenu
,
2349 MAKELPARAM(0, IS_SYSTEM_MENU(submenu
)) );
2354 /***********************************************************************
2357 * Display the sub-menu of the selected item of this menu.
2358 * Return the handle of the submenu, or hmenu if no submenu to display.
2360 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2361 BOOL selectFirst
, UINT wFlags
)
2368 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2370 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2372 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2374 item
= &menu
->items
[menu
->FocusedItem
];
2375 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2378 /* message must be sent before using item,
2379 because nearly everything may be changed by the application ! */
2381 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2382 if (!(wFlags
& TPM_NONOTIFY
))
2383 SendMessageW( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2384 MAKELPARAM( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2386 item
= &menu
->items
[menu
->FocusedItem
];
2389 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2390 if (!(item
->fState
& MF_HILITE
))
2392 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2393 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2395 SelectObject( hdc
, get_menu_font(FALSE
));
2397 item
->fState
|= MF_HILITE
;
2398 MENU_DrawMenuItem( menu
->hWnd
, menu
, hwndOwner
, hdc
, item
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2399 ReleaseDC( menu
->hWnd
, hdc
);
2401 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2404 item
->fState
|= MF_MOUSESELECT
;
2406 if (IS_SYSTEM_MENU(menu
))
2408 MENU_InitSysMenuPopup(item
->hSubMenu
,
2409 GetWindowLongW( menu
->hWnd
, GWL_STYLE
),
2410 GetClassLongW( menu
->hWnd
, GCL_STYLE
));
2412 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2413 if (wFlags
& TPM_LAYOUTRTL
) rect
.left
= rect
.right
;
2414 rect
.top
= rect
.bottom
;
2415 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2416 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2420 RECT item_rect
= item
->rect
;
2422 MENU_AdjustMenuItemRect(menu
, &item_rect
);
2423 GetWindowRect( menu
->hWnd
, &rect
);
2425 if (menu
->wFlags
& MF_POPUP
)
2427 /* The first item in the popup menu has to be at the
2428 same y position as the focused menu item */
2429 if (wFlags
& TPM_LAYOUTRTL
)
2430 rect
.left
+= GetSystemMetrics(SM_CXBORDER
);
2432 rect
.left
+= item_rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2433 rect
.top
+= item_rect
.top
- MENU_MARGIN
;
2434 rect
.right
= item_rect
.left
- item_rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2435 rect
.bottom
= item_rect
.top
- item_rect
.bottom
- 2 * MENU_MARGIN
;
2439 if (wFlags
& TPM_LAYOUTRTL
)
2440 rect
.left
= rect
.right
- item_rect
.left
;
2442 rect
.left
+= item_rect
.left
;
2443 rect
.top
+= item_rect
.bottom
;
2444 rect
.right
= item_rect
.right
- item_rect
.left
;
2445 rect
.bottom
= item_rect
.bottom
- item_rect
.top
;
2449 /* use default alignment for submenus */
2450 wFlags
&= ~(TPM_CENTERALIGN
| TPM_RIGHTALIGN
| TPM_VCENTERALIGN
| TPM_BOTTOMALIGN
);
2452 MENU_InitPopup( hwndOwner
, item
->hSubMenu
, wFlags
);
2454 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
, wFlags
,
2455 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2457 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2458 return item
->hSubMenu
;
2463 /**********************************************************************
2466 HWND
MENU_IsMenuActive(void)
2471 /**********************************************************************
2474 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2476 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2478 void MENU_EndMenu( HWND hwnd
)
2481 menu
= top_popup_hmenu
? MENU_GetMenu( top_popup_hmenu
) : NULL
;
2482 if (menu
&& (hwnd
== menu
->hWnd
|| hwnd
== menu
->hwndOwner
)) EndMenu();
2485 /***********************************************************************
2488 * Walks menu chain trying to find a menu pt maps to.
2490 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2492 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2493 UINT item
= menu
->FocusedItem
;
2496 /* try subpopup first (if any) */
2497 ret
= (item
!= NO_SELECTED_ITEM
&&
2498 (menu
->items
[item
].fType
& MF_POPUP
) &&
2499 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2500 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2502 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2504 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2505 if( menu
->wFlags
& MF_POPUP
)
2507 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2509 else if (ht
== HTSYSMENU
)
2510 ret
= get_win_sys_menu( menu
->hWnd
);
2511 else if (ht
== HTMENU
)
2512 ret
= GetMenu( menu
->hWnd
);
2517 /***********************************************************************
2518 * MENU_ExecFocusedItem
2520 * Execute a menu item (for instance when user pressed Enter).
2521 * Return the wID of the executed item. Otherwise, -1 indicating
2522 * that no menu item was executed, -2 if a popup is shown;
2523 * Have to receive the flags for the TrackPopupMenu options to avoid
2524 * sending unwanted message.
2527 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2530 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2532 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2534 if (!menu
|| !menu
->nItems
||
2535 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2537 item
= &menu
->items
[menu
->FocusedItem
];
2539 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu
, item
->wID
, item
->hSubMenu
, item
->fType
);
2541 if (!(item
->fType
& MF_POPUP
))
2543 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2545 /* If TPM_RETURNCMD is set you return the id, but
2546 do not send a message to the owner */
2547 if(!(wFlags
& TPM_RETURNCMD
))
2549 if( menu
->wFlags
& MF_SYSMENU
)
2550 PostMessageW( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2551 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2554 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2555 DWORD dwStyle
= menu
->dwStyle
| (topmenu
? topmenu
->dwStyle
: 0);
2557 if (dwStyle
& MNS_NOTIFYBYPOS
)
2558 PostMessageW( pmt
->hOwnerWnd
, WM_MENUCOMMAND
, menu
->FocusedItem
,
2561 PostMessageW( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2569 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2576 /***********************************************************************
2577 * MENU_SwitchTracking
2579 * Helper function for menu navigation routines.
2581 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
, UINT wFlags
)
2583 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2584 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2586 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2588 if( pmt
->hTopMenu
!= hPtMenu
&&
2589 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2591 /* both are top level menus (system and menu-bar) */
2592 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2593 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2594 pmt
->hTopMenu
= hPtMenu
;
2596 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2597 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2601 /***********************************************************************
2604 * Return TRUE if we can go on with menu tracking.
2606 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, UINT message
, HMENU hPtMenu
, UINT wFlags
)
2608 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2613 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2614 enum hittest ht
= ht_item
;
2616 if( IS_SYSTEM_MENU(ptmenu
) )
2618 if (message
== WM_LBUTTONDBLCLK
) return FALSE
;
2622 ht
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
);
2624 if (pos
!= NO_SELECTED_ITEM
)
2626 if (ptmenu
->FocusedItem
!= pos
)
2627 MENU_SwitchTracking( pmt
, hPtMenu
, pos
, wFlags
);
2629 /* If the popup menu is not already "popped" */
2630 if (!(ptmenu
->items
[pos
].fState
& MF_MOUSESELECT
))
2631 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2634 /* A click on an item or anywhere on a popup keeps tracking going */
2635 if (ht
== ht_item
|| ((ptmenu
->wFlags
& MF_POPUP
) && ht
!= ht_nowhere
))
2641 /***********************************************************************
2644 * Return the value of MENU_ExecFocusedItem if
2645 * the selected item was not a popup. Else open the popup.
2646 * A -1 return value indicates that we go on with menu tracking.
2649 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2651 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2656 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2658 if( IS_SYSTEM_MENU(ptmenu
) )
2660 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &pos
) != ht_item
)
2661 pos
= NO_SELECTED_ITEM
;
2663 if (pos
!= NO_SELECTED_ITEM
&& (ptmenu
->FocusedItem
== pos
))
2665 debug_print_menuitem ("FocusedItem: ", &ptmenu
->items
[pos
], "");
2667 if (!(ptmenu
->items
[pos
].fType
& MF_POPUP
))
2669 INT executedMenuId
= MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2670 if (executedMenuId
== -1 || executedMenuId
== -2) return -1;
2671 return executedMenuId
;
2674 /* If we are dealing with the menu bar */
2675 /* and this is a click on an already "popped" item: */
2676 /* Stop the menu tracking and close the opened submenus */
2677 if(((pmt
->hTopMenu
== hPtMenu
) || IS_SYSTEM_MENU(ptmenu
)) && (pmt
->trackFlags
& TF_RCVD_BTN_UP
))
2680 if( GetMenu(ptmenu
->hWnd
) == hPtMenu
|| IS_SYSTEM_MENU(ptmenu
) )
2682 if (pos
== NO_SELECTED_ITEM
) return 0;
2683 pmt
->trackFlags
|= TF_RCVD_BTN_UP
;
2690 /***********************************************************************
2693 * Return TRUE if we can go on with menu tracking.
2695 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2697 UINT id
= NO_SELECTED_ITEM
;
2698 POPUPMENU
*ptmenu
= NULL
;
2702 ptmenu
= MENU_GetMenu( hPtMenu
);
2703 if( IS_SYSTEM_MENU(ptmenu
) )
2705 else if (MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
) != ht_item
)
2706 id
= NO_SELECTED_ITEM
;
2709 if( id
== NO_SELECTED_ITEM
)
2711 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2712 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2715 else if( ptmenu
->FocusedItem
!= id
)
2717 MENU_SwitchTracking( pmt
, hPtMenu
, id
, wFlags
);
2718 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2724 /***********************************************************************
2727 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2729 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
, UINT wFlags
)
2731 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2734 /* When skipping left, we need to do something special after the
2736 if (vk
== VK_LEFT
&& menu
->FocusedItem
== 0)
2740 /* When skipping right, for the non-system menu, we need to
2741 handle the last non-special menu item (ie skip any window
2742 icons such as MDI maximize, restore or close) */
2743 else if ((vk
== VK_RIGHT
) && !IS_SYSTEM_MENU(menu
))
2745 UINT i
= menu
->FocusedItem
+ 1;
2746 while (i
< menu
->nItems
) {
2747 if ((menu
->items
[i
].wID
>= SC_SIZE
&&
2748 menu
->items
[i
].wID
<= SC_RESTORE
)) {
2752 if (i
== menu
->nItems
) {
2756 /* When skipping right, we need to cater for the system menu */
2757 else if ((vk
== VK_RIGHT
) && IS_SYSTEM_MENU(menu
))
2759 if (menu
->FocusedItem
== (menu
->nItems
- 1)) {
2766 MDINEXTMENU next_menu
;
2771 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2772 next_menu
.hmenuNext
= 0;
2773 next_menu
.hwndNext
= 0;
2774 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2776 TRACE("%p [%p] -> %p [%p]\n",
2777 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2779 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2781 DWORD style
= GetWindowLongW( pmt
->hOwnerWnd
, GWL_STYLE
);
2782 hNewWnd
= pmt
->hOwnerWnd
;
2783 if( IS_SYSTEM_MENU(menu
) )
2785 /* switch to the menu bar */
2787 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2791 menu
= MENU_GetMenu( hNewMenu
);
2792 id
= menu
->nItems
- 1;
2794 /* Skip backwards over any system predefined icons,
2795 eg. MDI close, restore etc icons */
2797 (menu
->items
[id
].wID
>= SC_SIZE
&&
2798 menu
->items
[id
].wID
<= SC_RESTORE
)) id
--;
2801 else if (style
& WS_SYSMENU
)
2803 /* switch to the system menu */
2804 hNewMenu
= get_win_sys_menu( hNewWnd
);
2808 else /* application returned a new menu to switch to */
2810 hNewMenu
= next_menu
.hmenuNext
;
2811 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2813 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2815 DWORD style
= GetWindowLongW( hNewWnd
, GWL_STYLE
);
2817 if (style
& WS_SYSMENU
&&
2818 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2820 /* get the real system menu */
2821 hNewMenu
= get_win_sys_menu(hNewWnd
);
2823 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2825 /* FIXME: Not sure what to do here;
2826 * perhaps try to track hNewMenu as a popup? */
2828 TRACE(" -- got confused.\n");
2835 if( hNewMenu
!= pmt
->hTopMenu
)
2837 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2839 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2840 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
2843 if( hNewWnd
!= pmt
->hOwnerWnd
)
2845 pmt
->hOwnerWnd
= hNewWnd
;
2846 set_capture_window( pmt
->hOwnerWnd
, GUI_INMENUMODE
, NULL
);
2849 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2850 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2857 /***********************************************************************
2860 * The idea is not to show the popup if the next input message is
2861 * going to hide it anyway.
2863 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT uMsg
)
2867 msg
.hwnd
= pmt
->hOwnerWnd
;
2869 PeekMessageW( &msg
, 0, uMsg
, uMsg
, PM_NOYIELD
| PM_REMOVE
);
2870 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2875 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2876 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2878 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2879 PeekMessageW( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2880 if( msg
.message
== WM_KEYDOWN
&&
2881 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2883 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2890 /* failures go through this */
2891 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2895 /***********************************************************************
2898 * Handle a VK_ESCAPE key event in a menu.
2900 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2902 BOOL bEndMenu
= TRUE
;
2904 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2906 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2908 if (menu
->wFlags
& MF_POPUP
)
2910 HMENU hmenutmp
, hmenuprev
;
2912 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2914 /* close topmost popup */
2915 while (hmenutmp
!= pmt
->hCurrentMenu
)
2917 hmenuprev
= hmenutmp
;
2918 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2921 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2922 pmt
->hCurrentMenu
= hmenuprev
;
2930 /***********************************************************************
2933 * Handle a VK_LEFT key event in a menu.
2935 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2938 HMENU hmenutmp
, hmenuprev
;
2941 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2942 menu
= MENU_GetMenu( hmenutmp
);
2944 /* Try to move 1 column left (if possible) */
2945 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2946 NO_SELECTED_ITEM
) {
2948 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2953 /* close topmost popup */
2954 while (hmenutmp
!= pmt
->hCurrentMenu
)
2956 hmenuprev
= hmenutmp
;
2957 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2960 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
, wFlags
);
2961 pmt
->hCurrentMenu
= hmenuprev
;
2963 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2965 /* move menu bar selection if no more popups are left */
2967 if( !MENU_DoNextMenu( pmt
, VK_LEFT
, wFlags
) )
2968 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2970 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2972 /* A sublevel menu was displayed - display the next one
2973 * unless there is another displacement coming up */
2975 if( !MENU_SuspendPopup( pmt
, msg
) )
2976 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2977 pmt
->hTopMenu
, TRUE
, wFlags
);
2983 /***********************************************************************
2986 * Handle a VK_RIGHT key event in a menu.
2988 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
, UINT msg
)
2991 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2994 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2996 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2997 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2999 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
3001 /* If already displaying a popup, try to display sub-popup */
3003 hmenutmp
= pmt
->hCurrentMenu
;
3004 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
3006 /* if subpopup was displayed then we are done */
3007 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
3010 /* Check to see if there's another column */
3011 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
3012 NO_SELECTED_ITEM
) {
3013 TRACE("Going to %d.\n", nextcol
);
3014 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
3019 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
3021 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
3023 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
, wFlags
);
3024 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
3025 } else hmenutmp
= 0;
3027 /* try to move to the next item */
3028 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
, wFlags
) )
3029 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
3031 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
3032 if( !MENU_SuspendPopup( pmt
, msg
) )
3033 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
3034 pmt
->hTopMenu
, TRUE
, wFlags
);
3038 static void CALLBACK
release_capture( BOOL __normal
)
3040 set_capture_window( 0, GUI_INMENUMODE
, NULL
);
3043 /***********************************************************************
3046 * Menu tracking code.
3048 static BOOL
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
3049 HWND hwnd
, const RECT
*lprect
)
3054 INT executedMenuId
= -1;
3056 BOOL enterIdleSent
= FALSE
;
3060 mt
.hCurrentMenu
= hmenu
;
3061 mt
.hTopMenu
= hmenu
;
3062 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
3066 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3067 hmenu
, wFlags
, x
, y
, hwnd
, wine_dbgstr_rect( lprect
));
3069 if (!(menu
= MENU_GetMenu( hmenu
)))
3071 WARN("Invalid menu handle %p\n", hmenu
);
3072 SetLastError(ERROR_INVALID_MENU_HANDLE
);
3076 if (wFlags
& TPM_BUTTONDOWN
)
3078 /* Get the result in order to start the tracking or not */
3079 fRemove
= MENU_ButtonDown( &mt
, WM_LBUTTONDOWN
, hmenu
, wFlags
);
3080 fEndMenu
= !fRemove
;
3083 if (wFlags
& TF_ENDMENU
) fEndMenu
= TRUE
;
3085 /* owner may not be visible when tracking a popup, so use the menu itself */
3086 capture_win
= (wFlags
& TPM_POPUPMENU
) ? menu
->hWnd
: mt
.hOwnerWnd
;
3087 set_capture_window( capture_win
, GUI_INMENUMODE
, NULL
);
3089 if ((wFlags
& TPM_POPUPMENU
) && menu
->nItems
== 0)
3092 __TRY
while (!fEndMenu
)
3094 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3095 if (!menu
) /* sometimes happens if I do a window manager close */
3098 /* we have to keep the message in the queue until it's
3099 * clear that menu loop is not over yet. */
3103 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
3105 if (!CallMsgFilterW( &msg
, MSGF_MENU
)) break;
3106 /* remove the message from the queue */
3107 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3113 HWND win
= menu
->wFlags
& MF_POPUP
? menu
->hWnd
: 0;
3114 enterIdleSent
= TRUE
;
3115 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
3121 /* check if EndMenu() tried to cancel us, by posting this message */
3122 if(msg
.message
== WM_CANCELMODE
)
3124 /* we are now out of the loop */
3127 /* remove the message from the queue */
3128 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3130 /* break out of internal loop, ala ESCAPE */
3136 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
3137 enterIdleSent
=FALSE
;
3140 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
3143 * Use the mouse coordinates in lParam instead of those in the MSG
3144 * struct to properly handle synthetic messages. They are already
3145 * in screen coordinates.
3147 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
3148 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
3150 /* Find a menu for this mouse event */
3151 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
3155 /* no WM_NC... messages in captured state */
3157 case WM_RBUTTONDBLCLK
:
3158 case WM_RBUTTONDOWN
:
3159 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3161 case WM_LBUTTONDBLCLK
:
3162 case WM_LBUTTONDOWN
:
3163 /* If the message belongs to the menu, removes it from the queue */
3164 /* Else, end menu tracking */
3165 fRemove
= MENU_ButtonDown( &mt
, msg
.message
, hmenu
, wFlags
);
3166 fEndMenu
= !fRemove
;
3170 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
3173 /* Check if a menu was selected by the mouse */
3176 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
3177 TRACE("executedMenuId %d\n", executedMenuId
);
3179 /* End the loop if executedMenuId is an item ID */
3180 /* or if the job was done (executedMenuId = 0). */
3181 fEndMenu
= fRemove
= (executedMenuId
!= -1);
3183 /* No menu was selected by the mouse */
3184 /* if the function was called by TrackPopupMenu, continue
3185 with the menu tracking. If not, stop it */
3187 fEndMenu
= !(wFlags
& TPM_POPUPMENU
);
3192 /* the selected menu item must be changed every time */
3193 /* the mouse moves. */
3196 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
3198 } /* switch(msg.message) - mouse */
3200 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
3202 fRemove
= TRUE
; /* Keyboard messages are always removed */
3216 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3217 NO_SELECTED_ITEM
, FALSE
, 0 );
3218 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3219 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
3223 case VK_DOWN
: /* If on menu bar, pull-down the menu */
3225 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
3226 if (!(menu
->wFlags
& MF_POPUP
))
3227 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
3228 else /* otherwise try to move selection */
3229 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3230 (msg
.wParam
== VK_UP
)? ITEM_PREV
: ITEM_NEXT
);
3234 MENU_KeyLeft( &mt
, wFlags
, msg
.message
);
3238 MENU_KeyRight( &mt
, wFlags
, msg
.message
);
3242 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
3248 hi
.cbSize
= sizeof(HELPINFO
);
3249 hi
.iContextType
= HELPINFO_MENUITEM
;
3250 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
3253 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
3254 hi
.hItemHandle
= hmenu
;
3255 hi
.dwContextId
= menu
->dwContextHelpID
;
3256 hi
.MousePos
= msg
.pt
;
3257 SendMessageW(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
3262 TranslateMessage( &msg
);
3265 break; /* WM_KEYDOWN */
3272 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
3274 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3275 fEndMenu
= (executedMenuId
!= -2);
3280 /* Hack to avoid control chars. */
3281 /* We will find a better way real soon... */
3282 if (msg
.wParam
< 32) break;
3284 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
3285 LOWORD(msg
.wParam
), FALSE
);
3286 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
3287 else if (pos
== (UINT
)-1) MessageBeep(0);
3290 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
3292 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
3293 fEndMenu
= (executedMenuId
!= -2);
3297 } /* switch(msg.message) - kbd */
3301 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3302 DispatchMessageW( &msg
);
3306 if (!fEndMenu
) fRemove
= TRUE
;
3308 /* finally remove message from the queue */
3310 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
3311 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
3312 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
3314 __FINALLY( release_capture
)
3316 /* If dropdown is still painted and the close box is clicked on
3317 then the menu will be destroyed as part of the DispatchMessage above.
3318 This will then invalidate the menu handle in mt.hTopMenu. We should
3319 check for this first. */
3320 if( IsMenu( mt
.hTopMenu
) )
3322 menu
= MENU_GetMenu( mt
.hTopMenu
);
3324 if( IsWindow( mt
.hOwnerWnd
) )
3326 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
, wFlags
);
3328 if (menu
&& (menu
->wFlags
& MF_POPUP
))
3330 DestroyWindow( menu
->hWnd
);
3333 if (!(wFlags
& TPM_NONOTIFY
))
3334 SendMessageW( mt
.hOwnerWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)mt
.hTopMenu
,
3335 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3337 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
3338 SendMessageW( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKEWPARAM(0,0xffff), 0 );
3342 SetLastError( ERROR_SUCCESS
);
3343 /* The return value is only used by TrackPopupMenu */
3344 if (!(wFlags
& TPM_RETURNCMD
)) return TRUE
;
3345 if (executedMenuId
== -1) executedMenuId
= 0;
3346 return executedMenuId
;
3349 /***********************************************************************
3352 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
3356 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
3360 if (!(menu
= MENU_GetMenu( hMenu
))) return FALSE
;
3362 /* This makes the menus of applications built with Delphi work.
3363 * It also enables menus to be displayed in more than one window,
3364 * but there are some bugs left that need to be fixed in this case.
3366 if (!bPopup
) menu
->hWnd
= hWnd
;
3369 top_popup
= menu
->hWnd
;
3370 top_popup_hmenu
= hMenu
;
3375 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3376 if (!(wFlags
& TPM_NONOTIFY
))
3377 SendMessageW( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
3379 SendMessageW( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
3381 if (!(wFlags
& TPM_NONOTIFY
))
3383 SendMessageW( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
3384 /* If an app changed/recreated menu bar entries in WM_INITMENU
3385 * menu sizes will be recalculated once the menu created/shown.
3392 /***********************************************************************
3395 static BOOL
MENU_ExitTracking(HWND hWnd
, BOOL bPopup
)
3397 TRACE("hwnd=%p\n", hWnd
);
3399 SendMessageW( hWnd
, WM_EXITMENULOOP
, bPopup
, 0 );
3402 top_popup_hmenu
= NULL
;
3406 /***********************************************************************
3407 * MENU_TrackMouseMenuBar
3409 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3411 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
3413 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3414 UINT wFlags
= TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3416 TRACE("wnd=%p ht=0x%04x %s\n", hWnd
, ht
, wine_dbgstr_point( &pt
));
3418 if (GetWindowLongW( hWnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3421 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
3423 /* fetch the window menu again, it may have changed */
3424 hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
3425 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
3426 MENU_ExitTracking(hWnd
, FALSE
);
3431 /***********************************************************************
3432 * MENU_TrackKbdMenuBar
3434 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3436 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, WCHAR wChar
)
3438 UINT uItem
= NO_SELECTED_ITEM
;
3440 UINT wFlags
= TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3442 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd
, wParam
, wChar
);
3444 /* find window that has a menu */
3446 while (is_win_menu_disallowed(hwnd
))
3447 if (!(hwnd
= GetAncestor( hwnd
, GA_PARENT
))) return;
3449 /* check if we have to track a system menu */
3451 hTrackMenu
= GetMenu( hwnd
);
3452 if (!hTrackMenu
|| IsIconic(hwnd
) || wChar
== ' ' )
3454 if (!(GetWindowLongW( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3455 hTrackMenu
= get_win_sys_menu( hwnd
);
3457 wParam
|= HTSYSMENU
; /* prevent item lookup */
3459 if (GetWindowLongW( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) wFlags
|= TPM_LAYOUTRTL
;
3461 if (!IsMenu( hTrackMenu
)) return;
3463 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3465 /* fetch the window menu again, it may have changed */
3466 hTrackMenu
= (wParam
& HTSYSMENU
) ? get_win_sys_menu( hwnd
) : GetMenu( hwnd
);
3468 if( wChar
&& wChar
!= ' ' )
3470 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, wChar
, (wParam
& HTSYSMENU
) );
3471 if ( uItem
>= (UINT
)(-2) )
3473 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3474 /* schedule end of menu tracking */
3475 wFlags
|= TF_ENDMENU
;
3480 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3482 if (!(wParam
& HTSYSMENU
) || wChar
== ' ')
3484 if( uItem
== NO_SELECTED_ITEM
)
3485 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3487 PostMessageW( hwnd
, WM_KEYDOWN
, VK_RETURN
, 0 );
3491 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3492 MENU_ExitTracking( hwnd
, FALSE
);
3495 /**********************************************************************
3496 * TrackPopupMenuEx (USER32.@)
3498 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3499 HWND hWnd
, LPTPMPARAMS lpTpm
)
3504 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3505 hMenu
, wFlags
, x
, y
, hWnd
, lpTpm
,
3506 lpTpm
? wine_dbgstr_rect( &lpTpm
->rcExclude
) : "-" );
3508 /* Parameter check */
3509 /* FIXME: this check is performed several times, here and in the called
3510 functions. That could be optimized */
3511 if (!(menu
= MENU_GetMenu( hMenu
)))
3513 SetLastError( ERROR_INVALID_MENU_HANDLE
);
3517 if (IsWindow(menu
->hWnd
))
3519 SetLastError( ERROR_POPUP_ALREADY_ACTIVE
);
3523 if (MENU_InitPopup( hWnd
, hMenu
, wFlags
))
3525 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3527 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3528 if (!(wFlags
& TPM_NONOTIFY
))
3529 SendMessageW( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3531 if (menu
->wFlags
& MF_SYSMENU
)
3532 MENU_InitSysMenuPopup( hMenu
, GetWindowLongW( hWnd
, GWL_STYLE
),
3533 GetClassLongW( hWnd
, GCL_STYLE
));
3535 if (MENU_ShowPopup( hWnd
, hMenu
, 0, wFlags
, x
, y
, 0, 0 ))
3536 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
,
3537 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3538 MENU_ExitTracking(hWnd
, TRUE
);
3542 DestroyWindow( menu
->hWnd
);
3545 if (!(wFlags
& TPM_NONOTIFY
))
3546 SendMessageW( hWnd
, WM_UNINITMENUPOPUP
, (WPARAM
)hMenu
,
3547 MAKELPARAM(0, IS_SYSTEM_MENU(menu
)) );
3555 /**********************************************************************
3556 * TrackPopupMenu (USER32.@)
3558 * Like the win32 API, the function return the command ID only if the
3559 * flag TPM_RETURNCMD is on.
3562 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3563 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3565 return TrackPopupMenuEx( hMenu
, wFlags
, x
, y
, hWnd
, NULL
);
3568 /***********************************************************************
3571 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3573 LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3575 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3581 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3582 SetWindowLongPtrW( hwnd
, 0, (LONG_PTR
)cs
->lpCreateParams
);
3586 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3587 return MA_NOACTIVATE
;
3592 BeginPaint( hwnd
, &ps
);
3593 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3594 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3595 EndPaint( hwnd
, &ps
);
3599 case WM_PRINTCLIENT
:
3601 MENU_DrawPopupMenu( hwnd
, (HDC
)wParam
,
3602 (HMENU
)GetWindowLongPtrW( hwnd
, 0 ) );
3610 /* zero out global pointer in case resident popup window was destroyed. */
3611 if (hwnd
== top_popup
) {
3613 top_popup_hmenu
= NULL
;
3621 if (!GetWindowLongPtrW( hwnd
, 0 )) ERR("no menu to display\n");
3624 SetWindowLongPtrW( hwnd
, 0, 0 );
3628 return GetWindowLongPtrW( hwnd
, 0 );
3631 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3637 /***********************************************************************
3638 * MENU_GetMenuBarHeight
3640 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3642 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3643 INT orgX
, INT orgY
)
3649 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3651 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3653 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3654 SelectObject( hdc
, get_menu_font(FALSE
));
3655 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3656 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3657 ReleaseDC( hwnd
, hdc
);
3658 return lppop
->Height
;
3662 /*******************************************************************
3663 * ChangeMenuA (USER32.@)
3665 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3666 UINT id
, UINT flags
)
3668 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3669 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3671 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3672 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3674 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3675 flags
& MF_BYPOSITION
? pos
: id
,
3676 flags
& ~MF_REMOVE
);
3677 /* Default: MF_INSERT */
3678 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3682 /*******************************************************************
3683 * ChangeMenuW (USER32.@)
3685 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3686 UINT id
, UINT flags
)
3688 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3689 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3691 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3692 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3694 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3695 flags
& MF_BYPOSITION
? pos
: id
,
3696 flags
& ~MF_REMOVE
);
3697 /* Default: MF_INSERT */
3698 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3702 /*******************************************************************
3703 * CheckMenuItem (USER32.@)
3705 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3712 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
3714 item
= &menu
->items
[pos
];
3716 ret
= item
->fState
& MF_CHECKED
;
3717 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3718 else item
->fState
&= ~MF_CHECKED
;
3719 release_menu_ptr(menu
);
3724 /**********************************************************************
3725 * EnableMenuItem (USER32.@)
3727 BOOL WINAPI
EnableMenuItem( HMENU hMenu
, UINT id
, UINT wFlags
)
3733 TRACE("(%p, %04x, %04x)\n", hMenu
, id
, wFlags
);
3735 /* Get the Popupmenu to access the owner menu */
3736 if (!(menu
= find_menu_item(hMenu
, id
, wFlags
, &pos
)))
3739 item
= &menu
->items
[pos
];
3740 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3741 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3743 /* If the close item in the system menu change update the close button */
3744 if ((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
) && menu
->hSysMenuOwner
)
3747 POPUPMENU
* parentMenu
;
3750 /* Get the parent menu to access */
3751 parentMenu
= grab_menu_ptr(menu
->hSysMenuOwner
);
3752 release_menu_ptr(menu
);
3756 hwnd
= parentMenu
->hWnd
;
3757 release_menu_ptr(parentMenu
);
3759 /* Refresh the frame to reflect the change */
3760 WIN_GetRectangles(hwnd
, COORDS_CLIENT
, &rc
, NULL
);
3762 RedrawWindow(hwnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3765 release_menu_ptr(menu
);
3771 /*******************************************************************
3772 * GetMenuStringA (USER32.@)
3774 INT WINAPI
GetMenuStringA(
3775 HMENU hMenu
, /* [in] menuhandle */
3776 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3777 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3778 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3779 UINT wFlags
/* [in] MF_ flags */
3787 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3788 if (str
&& nMaxSiz
) str
[0] = '\0';
3790 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3792 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3795 item
= &menu
->items
[pos
];
3799 else if (!str
|| !nMaxSiz
)
3800 ret
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3803 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3807 release_menu_ptr(menu
);
3809 TRACE("returning %s\n", debugstr_a(str
));
3814 /*******************************************************************
3815 * GetMenuStringW (USER32.@)
3817 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3818 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3825 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3826 if (str
&& nMaxSiz
) str
[0] = '\0';
3828 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3830 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3833 item
= &menu
->items
[pos
];
3835 if (!str
|| !nMaxSiz
)
3836 ret
= item
->text
? strlenW(item
->text
) : 0;
3837 else if (!item
->text
)
3844 lstrcpynW( str
, item
->text
, nMaxSiz
);
3847 release_menu_ptr(menu
);
3849 TRACE("returning %s\n", debugstr_w(str
));
3854 /**********************************************************************
3855 * HiliteMenuItem (USER32.@)
3857 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3865 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3867 if (!(menu
= find_menu_item(hMenu
, wItemID
, wHilite
, &pos
))) return FALSE
;
3869 handle_menu
= menu
->obj
.handle
;
3870 focused_item
= menu
->FocusedItem
;
3871 release_menu_ptr(menu
);
3873 if (focused_item
!= pos
)
3875 MENU_HideSubPopups( hWnd
, handle_menu
, FALSE
, 0 );
3876 MENU_SelectItem( hWnd
, handle_menu
, pos
, TRUE
, 0 );
3883 /**********************************************************************
3884 * GetMenuState (USER32.@)
3886 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3892 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3894 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3897 item
= &menu
->items
[pos
];
3898 debug_print_menuitem (" item: ", item
, "");
3899 if (item
->fType
& MF_POPUP
)
3901 POPUPMENU
*submenu
= grab_menu_ptr(item
->hSubMenu
);
3903 state
= (submenu
->nItems
<< 8) | ((item
->fState
| item
->fType
) & 0xff);
3906 release_menu_ptr(submenu
);
3910 /* We used to (from way back then) mask the result to 0xff. */
3911 /* I don't know why and it seems wrong as the documented */
3912 /* return flag MF_SEPARATOR is outside that mask. */
3913 state
= (item
->fType
| item
->fState
);
3915 release_menu_ptr(menu
);
3920 /**********************************************************************
3921 * GetMenuItemCount (USER32.@)
3923 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3925 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
3928 if (!menu
) return -1;
3929 count
= menu
->nItems
;
3930 release_menu_ptr(menu
);
3932 TRACE("(%p) returning %d\n", hMenu
, count
);
3937 /**********************************************************************
3938 * GetMenuItemID (USER32.@)
3940 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3945 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
3948 id
= menu
->items
[pos
].fType
& MF_POPUP
? -1 : menu
->items
[pos
].wID
;
3949 release_menu_ptr(menu
);
3954 /**********************************************************************
3957 * Uses flags, id and text ptr, passed by InsertMenu() and
3958 * ModifyMenu() to setup a MenuItemInfo structure.
3960 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3961 LPMENUITEMINFOW pmii
)
3963 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3964 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3965 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3966 /* setting bitmap clears text and vice versa */
3967 if( IS_STRING_ITEM(flags
)) {
3968 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3970 flags
|= MF_SEPARATOR
;
3971 /* Item beginning with a backspace is a help item */
3972 /* FIXME: wrong place, this is only true in win16 */
3973 else if( *str
== '\b') {
3977 pmii
->dwTypeData
= (LPWSTR
)str
;
3978 } else if( flags
& MFT_BITMAP
){
3979 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3980 pmii
->hbmpItem
= (HBITMAP
)str
;
3982 if( flags
& MF_OWNERDRAW
){
3983 pmii
->fMask
|= MIIM_DATA
;
3984 pmii
->dwItemData
= (ULONG_PTR
) str
;
3986 if( flags
& MF_POPUP
&& MENU_GetMenu((HMENU
)id
)) {
3987 pmii
->fMask
|= MIIM_SUBMENU
;
3988 pmii
->hSubMenu
= (HMENU
)id
;
3990 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3991 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3992 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3993 pmii
->wID
= (UINT
)id
;
3997 /*******************************************************************
3998 * InsertMenuW (USER32.@)
4000 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
4001 UINT_PTR id
, LPCWSTR str
)
4009 if (IS_STRING_ITEM(flags
) && str
)
4010 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
4011 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4012 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
4013 hMenu
, pos
, flags
, id
, str
);
4015 if (!(menu
= insert_menu_item(hMenu
, pos
, flags
, &newpos
)))
4018 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4020 item
= &menu
->items
[newpos
];
4021 ret
= SetMenuItemInfo_common( item
, &mii
, TRUE
);
4023 item
->hCheckBit
= item
->hUnCheckBit
= 0;
4025 RemoveMenu( hMenu
, pos
, flags
);
4026 release_menu_ptr(menu
);
4032 /*******************************************************************
4033 * InsertMenuA (USER32.@)
4035 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4036 UINT_PTR id
, LPCSTR str
)
4040 if (IS_STRING_ITEM(flags
) && str
)
4042 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4043 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4046 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4047 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
4048 HeapFree( GetProcessHeap(), 0, newstr
);
4052 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4056 /*******************************************************************
4057 * AppendMenuA (USER32.@)
4059 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
4060 UINT_PTR id
, LPCSTR data
)
4062 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4066 /*******************************************************************
4067 * AppendMenuW (USER32.@)
4069 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
4070 UINT_PTR id
, LPCWSTR data
)
4072 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4076 /**********************************************************************
4077 * RemoveMenu (USER32.@)
4079 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT id
, UINT flags
)
4084 TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu
, id
, flags
);
4086 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4090 MENU_FreeItemData( &menu
->items
[pos
] );
4092 if (--menu
->nItems
== 0)
4094 HeapFree( GetProcessHeap(), 0, menu
->items
);
4099 MENUITEM
*new_items
, *item
= &menu
->items
[pos
];
4101 while (pos
< menu
->nItems
)
4107 new_items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
, menu
->nItems
* sizeof(MENUITEM
) );
4108 if (new_items
) menu
->items
= new_items
;
4110 release_menu_ptr(menu
);
4116 /**********************************************************************
4117 * DeleteMenu (USER32.@)
4119 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT id
, UINT flags
)
4124 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4127 if (menu
->items
[pos
].fType
& MF_POPUP
)
4128 DestroyMenu(menu
->items
[pos
].hSubMenu
);
4130 RemoveMenu(menu
->obj
.handle
, pos
, flags
| MF_BYPOSITION
);
4131 release_menu_ptr(menu
);
4136 /*******************************************************************
4137 * ModifyMenuW (USER32.@)
4139 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
4140 UINT_PTR id
, LPCWSTR str
)
4147 if (IS_STRING_ITEM(flags
))
4148 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4150 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
4152 if (!(menu
= find_menu_item(hMenu
, pos
, flags
, &item_pos
)))
4154 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4155 if (pos
== SC_TASKLIST
&& !(flags
& MF_BYPOSITION
)) return TRUE
;
4158 menu
->Height
= 0; /* force size recalculate */
4159 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4160 ret
= SetMenuItemInfo_common(&menu
->items
[item_pos
], &mii
, TRUE
);
4161 release_menu_ptr(menu
);
4166 /*******************************************************************
4167 * ModifyMenuA (USER32.@)
4169 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4170 UINT_PTR id
, LPCSTR str
)
4174 if (IS_STRING_ITEM(flags
) && str
)
4176 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4177 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4180 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4181 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
4182 HeapFree( GetProcessHeap(), 0, newstr
);
4186 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4190 /**********************************************************************
4191 * CreatePopupMenu (USER32.@)
4193 HMENU WINAPI
CreatePopupMenu(void)
4198 if (!(hmenu
= CreateMenu())) return 0;
4199 menu
= MENU_GetMenu( hmenu
);
4200 menu
->wFlags
|= MF_POPUP
;
4205 /**********************************************************************
4206 * GetMenuCheckMarkDimensions (USER.417)
4207 * GetMenuCheckMarkDimensions (USER32.@)
4209 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4211 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4215 /**********************************************************************
4216 * SetMenuItemBitmaps (USER32.@)
4218 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4219 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4225 if (!(menu
= find_menu_item(hMenu
, nPos
, wFlags
, &pos
)))
4228 item
= &menu
->items
[pos
];
4229 if (!hNewCheck
&& !hNewUnCheck
)
4231 item
->fState
&= ~MF_USECHECKBITMAPS
;
4233 else /* Install new bitmaps */
4235 item
->hCheckBit
= hNewCheck
;
4236 item
->hUnCheckBit
= hNewUnCheck
;
4237 item
->fState
|= MF_USECHECKBITMAPS
;
4239 release_menu_ptr(menu
);
4245 /**********************************************************************
4246 * CreateMenu (USER32.@)
4248 HMENU WINAPI
CreateMenu(void)
4253 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4254 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4257 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4259 TRACE("return %p\n", hMenu
);
4265 /**********************************************************************
4266 * DestroyMenu (USER32.@)
4268 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4272 TRACE("(%p)\n", hMenu
);
4274 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4275 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4277 /* DestroyMenu should not destroy system menu popup owner */
4278 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4280 DestroyWindow( lppop
->hWnd
);
4284 if (lppop
->items
) /* recursively destroy submenus */
4287 MENUITEM
*item
= lppop
->items
;
4288 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4290 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4291 MENU_FreeItemData( item
);
4293 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4295 HeapFree( GetProcessHeap(), 0, lppop
);
4300 /**********************************************************************
4301 * GetSystemMenu (USER32.@)
4303 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4305 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4308 if (wndPtr
== WND_DESKTOP
) return 0;
4309 if (wndPtr
== WND_OTHER_PROCESS
)
4311 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4315 if (wndPtr
->hSysMenu
&& bRevert
)
4317 DestroyMenu(wndPtr
->hSysMenu
);
4318 wndPtr
->hSysMenu
= 0;
4321 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4322 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4324 if( wndPtr
->hSysMenu
)
4327 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4329 /* Store the dummy sysmenu handle to facilitate the refresh */
4330 /* of the close button if the SC_CLOSE item change */
4331 menu
= MENU_GetMenu(retvalue
);
4333 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4335 WIN_ReleasePtr( wndPtr
);
4337 return bRevert
? 0 : retvalue
;
4341 /*******************************************************************
4342 * SetSystemMenu (USER32.@)
4344 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4346 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4348 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4350 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4351 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4352 WIN_ReleasePtr( wndPtr
);
4359 /**********************************************************************
4360 * GetMenu (USER32.@)
4362 HMENU WINAPI
GetMenu( HWND hWnd
)
4364 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4365 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4369 /**********************************************************************
4370 * GetMenuBarInfo (USER32.@)
4372 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4378 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4383 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
4386 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
4388 WARN("called on invalid window: %d\n", class_atom
);
4389 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4393 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
4396 hmenu
= GetMenu(hwnd
);
4399 hmenu
= GetSystemMenu(hwnd
, FALSE
);
4408 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
4410 SetLastError(ERROR_INVALID_PARAMETER
);
4414 menu
= MENU_GetMenu(hmenu
);
4417 if (idItem
< 0 || idItem
> menu
->nItems
)
4422 SetRectEmpty(&pmbi
->rcBar
);
4424 else if (idItem
== 0)
4426 GetMenuItemRect(hwnd
, hmenu
, 0, &pmbi
->rcBar
);
4427 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
4428 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4432 GetMenuItemRect(hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4435 pmbi
->hMenu
= hmenu
;
4436 pmbi
->hwndMenu
= NULL
;
4437 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4440 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4441 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4443 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4445 pmbi
->hwndMenu
= menu
->hWnd
;
4450 pmbi
->fFocused
= pmbi
->fBarFocused
;
4456 /**********************************************************************
4459 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4460 * SetWindowPos call that would result if SetMenu were called directly.
4462 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4464 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4466 if (hMenu
&& !IsMenu(hMenu
))
4468 WARN("hMenu %p is not a menu handle\n", hMenu
);
4471 if (is_win_menu_disallowed(hWnd
))
4474 hWnd
= WIN_GetFullHandle( hWnd
);
4475 if (GetCapture() == hWnd
)
4476 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4482 if (!(menu
= grab_menu_ptr(hMenu
))) return FALSE
;
4484 menu
->Height
= 0; /* Make sure we recalculate the size */
4485 release_menu_ptr(menu
);
4487 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4492 /**********************************************************************
4493 * SetMenu (USER32.@)
4495 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4497 if(!MENU_SetMenu(hWnd
, hMenu
))
4500 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4501 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4506 /**********************************************************************
4507 * GetSubMenu (USER32.@)
4509 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4515 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
4518 if (menu
->items
[pos
].fType
& MF_POPUP
)
4519 submenu
= menu
->items
[pos
].hSubMenu
;
4523 release_menu_ptr(menu
);
4528 /**********************************************************************
4529 * DrawMenuBar (USER32.@)
4531 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4535 if (!IsWindow( hWnd
))
4537 if (is_win_menu_disallowed(hWnd
))
4540 if ((hMenu
= GetMenu( hWnd
)))
4542 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
4545 menu
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4546 menu
->hwndOwner
= hWnd
;
4547 release_menu_ptr(menu
);
4551 return SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4552 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4555 /***********************************************************************
4556 * DrawMenuBarTemp (USER32.@)
4560 * called by W98SE desk.cpl Control Panel Applet
4562 * Not 100% sure about the param names, but close.
4564 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4569 BOOL flat_menu
= FALSE
;
4571 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4574 hMenu
= GetMenu(hwnd
);
4577 hFont
= get_menu_font(FALSE
);
4579 lppop
= MENU_GetMenu( hMenu
);
4580 if (lppop
== NULL
|| lprect
== NULL
)
4582 retvalue
= GetSystemMetrics(SM_CYMENU
);
4586 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4588 hfontOld
= SelectObject( hDC
, hFont
);
4590 if (lppop
->Height
== 0)
4591 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4593 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4595 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4597 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4598 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4599 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4601 if (lppop
->nItems
== 0)
4603 retvalue
= GetSystemMetrics(SM_CYMENU
);
4607 for (i
= 0; i
< lppop
->nItems
; i
++)
4608 MENU_DrawMenuItem( hwnd
, lppop
, hwnd
, hDC
, &lppop
->items
[i
], TRUE
, ODA_DRAWENTIRE
);
4610 retvalue
= lppop
->Height
;
4613 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4617 /***********************************************************************
4618 * EndMenu (USER.187)
4619 * EndMenu (USER32.@)
4621 BOOL WINAPI
EndMenu(void)
4623 /* if we are in the menu code, and it is active */
4624 if (!fEndMenu
&& top_popup
)
4626 /* terminate the menu handling code */
4629 /* needs to be posted to wakeup the internal menu handler */
4630 /* which will now terminate the menu, in the event that */
4631 /* the main window was minimized, or lost focus, so we */
4632 /* don't end up with an orphaned menu */
4633 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4639 /*****************************************************************
4640 * LoadMenuA (USER32.@)
4642 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4644 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4645 if (!hrsrc
) return 0;
4646 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4650 /*****************************************************************
4651 * LoadMenuW (USER32.@)
4653 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4655 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4656 if (!hrsrc
) return 0;
4657 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4661 /**********************************************************************
4662 * LoadMenuIndirectW (USER32.@)
4664 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4667 WORD version
, offset
;
4668 LPCSTR p
= template;
4670 version
= GET_WORD(p
);
4672 TRACE("%p, ver %d\n", template, version
);
4675 case 0: /* standard format is version of 0 */
4676 offset
= GET_WORD(p
);
4677 p
+= sizeof(WORD
) + offset
;
4678 if (!(hMenu
= CreateMenu())) return 0;
4679 if (!MENU_ParseResource( p
, hMenu
))
4681 DestroyMenu( hMenu
);
4685 case 1: /* extended format is version of 1 */
4686 offset
= GET_WORD(p
);
4687 p
+= sizeof(WORD
) + offset
;
4688 if (!(hMenu
= CreateMenu())) return 0;
4689 if (!MENUEX_ParseResource( p
, hMenu
))
4691 DestroyMenu( hMenu
);
4696 ERR("version %d not supported.\n", version
);
4702 /**********************************************************************
4703 * LoadMenuIndirectA (USER32.@)
4705 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4707 return LoadMenuIndirectW( template );
4711 /**********************************************************************
4714 BOOL WINAPI
IsMenu(HMENU hmenu
)
4719 menu
= grab_menu_ptr(hmenu
);
4720 is_menu
= menu
!= NULL
;
4721 release_menu_ptr(menu
);
4724 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4729 /**********************************************************************
4730 * GetMenuItemInfo_common
4733 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT id
, BOOL bypos
,
4734 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4740 menu
= find_menu_item(hmenu
, id
, bypos
? MF_BYPOSITION
: 0, &pos
);
4742 item
= menu
? &menu
->items
[pos
] : NULL
;
4744 debug_print_menuitem("GetMenuItemInfo_common: ", item
, "");
4748 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4752 if( lpmii
->fMask
& MIIM_TYPE
) {
4753 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4754 release_menu_ptr(menu
);
4755 WARN("invalid combination of fMask bits used\n");
4756 /* this does not happen on Win9x/ME */
4757 SetLastError( ERROR_INVALID_PARAMETER
);
4760 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4761 if (item
->hbmpItem
&& !IS_MAGIC_BITMAP(item
->hbmpItem
))
4762 lpmii
->fType
|= MFT_BITMAP
;
4763 lpmii
->hbmpItem
= item
->hbmpItem
; /* not on Win9x/ME */
4764 if( lpmii
->fType
& MFT_BITMAP
) {
4765 lpmii
->dwTypeData
= (LPWSTR
) item
->hbmpItem
;
4767 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4768 /* this does not happen on Win9x/ME */
4769 lpmii
->dwTypeData
= 0;
4774 /* copy the text string */
4775 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4777 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4779 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4781 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4788 len
= strlenW(item
->text
);
4789 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4790 lstrcpynW(lpmii
->dwTypeData
, item
->text
, lpmii
->cch
);
4794 len
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
,
4795 0, NULL
, NULL
) - 1;
4796 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4797 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1,
4798 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4799 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4801 /* if we've copied a substring we return its length */
4802 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4803 if (lpmii
->cch
<= len
+ 1)
4808 /* return length of string */
4809 /* not on Win9x/ME if fType & MFT_BITMAP */
4815 if (lpmii
->fMask
& MIIM_FTYPE
)
4816 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4818 if (lpmii
->fMask
& MIIM_BITMAP
)
4819 lpmii
->hbmpItem
= item
->hbmpItem
;
4821 if (lpmii
->fMask
& MIIM_STATE
)
4822 lpmii
->fState
= item
->fState
& MENUITEMINFO_STATE_MASK
;
4824 if (lpmii
->fMask
& MIIM_ID
)
4825 lpmii
->wID
= item
->wID
;
4827 if (lpmii
->fMask
& MIIM_SUBMENU
)
4828 lpmii
->hSubMenu
= item
->hSubMenu
;
4830 /* hSubMenu is always cleared
4831 * (not on Win9x/ME ) */
4832 lpmii
->hSubMenu
= 0;
4835 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4836 lpmii
->hbmpChecked
= item
->hCheckBit
;
4837 lpmii
->hbmpUnchecked
= item
->hUnCheckBit
;
4839 if (lpmii
->fMask
& MIIM_DATA
)
4840 lpmii
->dwItemData
= item
->dwItemData
;
4842 release_menu_ptr(menu
);
4846 /**********************************************************************
4847 * GetMenuItemInfoA (USER32.@)
4849 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4850 LPMENUITEMINFOA lpmii
)
4854 if( lpmii
->cbSize
!= sizeof( mii
) &&
4855 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4856 SetLastError( ERROR_INVALID_PARAMETER
);
4859 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4860 mii
.cbSize
= sizeof( mii
);
4861 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4862 (LPMENUITEMINFOW
)&mii
, FALSE
);
4863 mii
.cbSize
= lpmii
->cbSize
;
4864 memcpy( lpmii
, &mii
, mii
.cbSize
);
4868 /**********************************************************************
4869 * GetMenuItemInfoW (USER32.@)
4871 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4872 LPMENUITEMINFOW lpmii
)
4876 if( lpmii
->cbSize
!= sizeof( mii
) &&
4877 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4878 SetLastError( ERROR_INVALID_PARAMETER
);
4881 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4882 mii
.cbSize
= sizeof( mii
);
4883 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4884 mii
.cbSize
= lpmii
->cbSize
;
4885 memcpy( lpmii
, &mii
, mii
.cbSize
);
4890 /* set a menu item text from a ASCII or Unicode string */
4891 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4897 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4898 strcpyW( menu
->text
, text
);
4902 LPCSTR str
= (LPCSTR
)text
;
4903 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4904 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4905 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4910 /**********************************************************************
4913 * detect if there are loops in the menu tree (or the depth is too large)
4915 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4922 if( depth
> MAXMENUDEPTH
) return depth
;
4923 item
= pmenu
->items
;
4925 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4926 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4928 int bdepth
= MENU_depth( psubmenu
, depth
);
4929 if( bdepth
> subdepth
) subdepth
= bdepth
;
4931 if( subdepth
> MAXMENUDEPTH
)
4932 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4938 /**********************************************************************
4939 * SetMenuItemInfo_common
4941 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4942 * MIIM_BITMAP and MIIM_STRING flags instead.
4945 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4946 const MENUITEMINFOW
*lpmii
,
4949 if (!menu
) return FALSE
;
4951 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4953 if (lpmii
->fMask
& MIIM_FTYPE
) {
4954 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4955 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4957 if (lpmii
->fMask
& MIIM_STRING
) {
4958 /* free the string when used */
4959 HeapFree(GetProcessHeap(), 0, menu
->text
);
4960 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4963 if (lpmii
->fMask
& MIIM_STATE
)
4964 /* Other menu items having MFS_DEFAULT are not converted
4966 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4968 if (lpmii
->fMask
& MIIM_ID
)
4969 menu
->wID
= lpmii
->wID
;
4971 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4972 menu
->hSubMenu
= lpmii
->hSubMenu
;
4973 if (menu
->hSubMenu
) {
4974 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4976 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4977 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4981 subMenu
->wFlags
|= MF_POPUP
;
4982 menu
->fType
|= MF_POPUP
;
4984 SetLastError( ERROR_INVALID_PARAMETER
);
4989 menu
->fType
&= ~MF_POPUP
;
4992 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4994 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4995 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4997 if (lpmii
->fMask
& MIIM_DATA
)
4998 menu
->dwItemData
= lpmii
->dwItemData
;
5000 if (lpmii
->fMask
& MIIM_BITMAP
)
5001 menu
->hbmpItem
= lpmii
->hbmpItem
;
5003 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
5004 menu
->fType
|= MFT_SEPARATOR
;
5006 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
5010 /**********************************************************************
5011 * MENU_NormalizeMenuItemInfoStruct
5013 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
5014 * check, copy and extend the MENUITEMINFO struct from the version that the application
5015 * supplied to the version used by wine source. */
5016 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
5017 MENUITEMINFOW
*pmii_out
)
5019 /* do we recognize the size? */
5020 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
5021 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
5022 SetLastError( ERROR_INVALID_PARAMETER
);
5025 /* copy the fields that we have */
5026 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
5027 /* if the hbmpItem member is missing then extend */
5028 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
5029 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
5030 pmii_out
->hbmpItem
= NULL
;
5032 /* test for invalid bit combinations */
5033 if( (pmii_out
->fMask
& MIIM_TYPE
&&
5034 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
5035 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
5036 WARN("invalid combination of fMask bits used\n");
5037 /* this does not happen on Win9x/ME */
5038 SetLastError( ERROR_INVALID_PARAMETER
);
5041 /* convert old style (MIIM_TYPE) to the new */
5042 if( pmii_out
->fMask
& MIIM_TYPE
){
5043 pmii_out
->fMask
|= MIIM_FTYPE
;
5044 if( IS_STRING_ITEM(pmii_out
->fType
)){
5045 pmii_out
->fMask
|= MIIM_STRING
;
5046 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
5047 pmii_out
->fMask
|= MIIM_BITMAP
;
5048 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
5054 /**********************************************************************
5055 * SetMenuItemInfoA (USER32.@)
5057 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
5058 const MENUITEMINFOA
*lpmii
)
5065 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5067 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5069 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5071 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5072 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5075 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5076 release_menu_ptr(menu
);
5080 /**********************************************************************
5081 * SetMenuItemInfoW (USER32.@)
5083 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
5084 const MENUITEMINFOW
*lpmii
)
5091 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5093 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5095 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5097 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5098 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5102 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5103 release_menu_ptr(menu
);
5107 static BOOL
set_menu_default_item(POPUPMENU
*menu
, UINT uItem
, UINT bypos
)
5112 /* reset all default-item flags */
5114 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5116 item
->fState
&= ~MFS_DEFAULT
;
5119 /* no default item */
5126 if ( uItem
>= menu
->nItems
) return FALSE
;
5127 item
[uItem
].fState
|= MFS_DEFAULT
;
5132 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5134 if (item
->wID
== uItem
)
5136 item
->fState
|= MFS_DEFAULT
;
5145 /**********************************************************************
5146 * SetMenuDefaultItem (USER32.@)
5149 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
5154 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
5156 if (!(menu
= grab_menu_ptr(hmenu
))) return FALSE
;
5157 ret
= set_menu_default_item(menu
, uItem
, bypos
);
5158 release_menu_ptr(menu
);
5163 /**********************************************************************
5164 * GetMenuDefaultItem (USER32.@)
5166 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
5172 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
5174 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
5176 /* find default item */
5180 if (! item
) return -1;
5182 while ( !( item
->fState
& MFS_DEFAULT
) )
5185 if (i
>= menu
->nItems
) return -1;
5188 /* default: don't return disabled items */
5189 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
5191 /* search rekursiv when needed */
5192 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
5195 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
5196 if ( -1 != ret
) return ret
;
5198 /* when item not found in submenu, return the popup item */
5200 return ( bypos
) ? i
: item
->wID
;
5205 /**********************************************************************
5206 * InsertMenuItemA (USER32.@)
5208 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5209 const MENUITEMINFOA
*lpmii
)
5216 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5218 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5220 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5223 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5224 release_menu_ptr(menu
);
5229 /**********************************************************************
5230 * InsertMenuItemW (USER32.@)
5232 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5233 const MENUITEMINFOW
*lpmii
)
5240 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5242 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5244 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5247 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5248 release_menu_ptr(menu
);
5252 /**********************************************************************
5253 * CheckMenuRadioItem (USER32.@)
5256 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
, UINT first
, UINT last
,
5257 UINT check
, UINT flags
)
5259 POPUPMENU
*first_menu
= NULL
, *check_menu
;
5263 for (i
= first
; i
<= last
; i
++)
5267 if (!(check_menu
= find_menu_item(hMenu
, i
, flags
, &check_pos
)))
5271 first_menu
= grab_menu_ptr(check_menu
->obj
.handle
);
5273 if (first_menu
!= check_menu
)
5275 release_menu_ptr(check_menu
);
5279 item
= &check_menu
->items
[check_pos
];
5280 if (item
->fType
!= MFT_SEPARATOR
)
5284 item
->fType
|= MFT_RADIOCHECK
;
5285 item
->fState
|= MFS_CHECKED
;
5290 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5291 item
->fState
&= ~MFS_CHECKED
;
5295 release_menu_ptr(check_menu
);
5297 release_menu_ptr(first_menu
);
5303 /**********************************************************************
5304 * GetMenuItemRect (USER32.@)
5306 * ATTENTION: Here, the returned values in rect are the screen
5307 * coordinates of the item just like if the menu was
5308 * always on the upper left side of the application.
5311 BOOL WINAPI
GetMenuItemRect(HWND hwnd
, HMENU hMenu
, UINT uItem
, RECT
*rect
)
5317 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5322 if (!(menu
= find_menu_item(hMenu
, uItem
, MF_BYPOSITION
, &pos
)))
5325 if (!hwnd
) hwnd
= menu
->hWnd
;
5328 release_menu_ptr(menu
);
5332 *rect
= menu
->items
[pos
].rect
;
5333 OffsetRect(rect
, menu
->items_rect
.left
, menu
->items_rect
.top
);
5335 /* Popup menu item draws in the client area */
5336 if (menu
->wFlags
& MF_POPUP
) MapWindowPoints(hwnd
, 0, (POINT
*)rect
, 2);
5339 /* Sysmenu draws in the non-client area */
5340 GetWindowRect(hwnd
, &window_rect
);
5341 OffsetRect(rect
, window_rect
.left
, window_rect
.top
);
5344 release_menu_ptr(menu
);
5348 /**********************************************************************
5349 * SetMenuInfo (USER32.@)
5352 * actually use the items to draw the menu
5353 * (recalculate and/or redraw)
5355 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5358 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5360 if (lpmi
->fMask
& MIM_BACKGROUND
)
5361 menu
->hbrBack
= lpmi
->hbrBack
;
5363 if (lpmi
->fMask
& MIM_HELPID
)
5364 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5366 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5367 menu
->cyMax
= lpmi
->cyMax
;
5369 if (lpmi
->fMask
& MIM_MENUDATA
)
5370 menu
->dwMenuData
= lpmi
->dwMenuData
;
5372 if (lpmi
->fMask
& MIM_STYLE
)
5373 menu
->dwStyle
= lpmi
->dwStyle
;
5375 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5377 MENUITEM
*item
= menu
->items
;
5378 for( i
= menu
->nItems
; i
; i
--, item
++)
5379 if( item
->fType
& MF_POPUP
)
5380 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5385 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5387 TRACE("(%p %p)\n", hMenu
, lpmi
);
5388 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5389 if( lpmi
->fMask
& MIM_STYLE
) {
5390 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5391 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5392 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5396 SetLastError( ERROR_INVALID_PARAMETER
);
5400 /**********************************************************************
5401 * GetMenuInfo (USER32.@)
5407 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5411 TRACE("(%p %p)\n", hMenu
, lpmi
);
5413 if (lpmi
&& (lpmi
->cbSize
== sizeof(MENUINFO
)) && (menu
= grab_menu_ptr(hMenu
)))
5415 if (lpmi
->fMask
& MIM_BACKGROUND
)
5416 lpmi
->hbrBack
= menu
->hbrBack
;
5418 if (lpmi
->fMask
& MIM_HELPID
)
5419 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5421 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5422 lpmi
->cyMax
= menu
->cyMax
;
5424 if (lpmi
->fMask
& MIM_MENUDATA
)
5425 lpmi
->dwMenuData
= menu
->dwMenuData
;
5427 if (lpmi
->fMask
& MIM_STYLE
)
5428 lpmi
->dwStyle
= menu
->dwStyle
;
5430 release_menu_ptr(menu
);
5433 SetLastError( ERROR_INVALID_PARAMETER
);
5438 /**********************************************************************
5439 * SetMenuContextHelpId (USER32.@)
5441 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5445 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5447 if ((menu
= grab_menu_ptr(hMenu
)))
5449 menu
->dwContextHelpID
= dwContextHelpID
;
5450 release_menu_ptr(menu
);
5457 /**********************************************************************
5458 * GetMenuContextHelpId (USER32.@)
5460 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5465 TRACE("(%p)\n", hMenu
);
5467 if ((menu
= grab_menu_ptr(hMenu
)))
5469 help_id
= menu
->dwContextHelpID
;
5470 release_menu_ptr(menu
);
5476 /**********************************************************************
5477 * MenuItemFromPoint (USER32.@)
5479 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5481 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
5484 /*FIXME: Do we have to handle hWnd here? */
5485 if (!menu
) return -1;
5487 if (MENU_FindItemByCoords( menu
, ptScreen
, &pos
) != ht_item
)
5490 release_menu_ptr(menu
);
5495 /**********************************************************************
5496 * CalcMenuBar (USER32.@)
5498 DWORD WINAPI
CalcMenuBar(HWND hwnd
, DWORD left
, DWORD right
, DWORD top
, RECT
*rect
)
5500 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd
, left
, right
, top
, rect
);
5505 /**********************************************************************
5506 * translate_accelerator
5508 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5509 BYTE fVirt
, WORD key
, WORD cmd
)
5514 if (wParam
!= key
) return FALSE
;
5516 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5517 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5518 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5520 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5522 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5524 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5530 if(fVirt
& FVIRTKEY
)
5532 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5533 wParam
, 0xff & HIWORD(lParam
));
5535 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5536 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5540 if (!(lParam
& 0x01000000)) /* no special_key */
5542 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5543 { /* ^^ ALT pressed */
5544 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5553 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5557 HMENU hMenu
, hSubMenu
, hSysMenu
;
5558 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5561 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5562 hSysMenu
= get_win_sys_menu( hWnd
);
5564 /* find menu item and ask application to initialize it */
5565 /* 1. in the system menu */
5566 if ((menu
= find_menu_item(hSysMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5568 hSubMenu
= menu
->obj
.handle
;
5569 release_menu_ptr(menu
);
5573 if (!IsWindowEnabled(hWnd
))
5577 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5578 if(hSubMenu
!= hSysMenu
)
5580 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5581 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5582 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5584 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5587 else /* 2. in the window's menu */
5589 if ((menu
= find_menu_item(hMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5591 hSubMenu
= menu
->obj
.handle
;
5592 release_menu_ptr(menu
);
5596 if (!IsWindowEnabled(hWnd
))
5600 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5601 if(hSubMenu
!= hMenu
)
5603 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5604 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5605 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5607 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5614 if (uSysStat
!= (UINT
)-1)
5616 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5623 if (uStat
!= (UINT
)-1)
5629 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5641 if( mesg
==WM_COMMAND
)
5643 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5644 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5646 else if( mesg
==WM_SYSCOMMAND
)
5648 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5649 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5653 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5654 * #0: unknown (please report!)
5655 * #1: for WM_KEYUP,WM_SYSKEYUP
5656 * #2: mouse is captured
5657 * #3: window is disabled
5658 * #4: it's a disabled system menu option
5659 * #5: it's a menu option, but window is iconic
5660 * #6: it's a menu option, but disabled
5662 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5664 ERR_(accel
)(" unknown reason - please report!\n");
5669 /**********************************************************************
5670 * TranslateAcceleratorA (USER32.@)
5671 * TranslateAccelerator (USER32.@)
5673 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5675 switch (msg
->message
)
5679 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5685 char ch
= LOWORD(msg
->wParam
);
5687 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5688 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5689 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5697 /**********************************************************************
5698 * TranslateAcceleratorW (USER32.@)
5700 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5702 ACCEL data
[32], *ptr
= data
;
5705 if (!hWnd
) return 0;
5707 if (msg
->message
!= WM_KEYDOWN
&&
5708 msg
->message
!= WM_SYSKEYDOWN
&&
5709 msg
->message
!= WM_CHAR
&&
5710 msg
->message
!= WM_SYSCHAR
)
5713 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5714 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5716 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5717 if (count
> ARRAY_SIZE( data
))
5719 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5721 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5722 for (i
= 0; i
< count
; i
++)
5724 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5725 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5728 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);