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
))
3746 if (menu
->hSysMenuOwner
)
3749 POPUPMENU
* parentMenu
;
3752 /* Get the parent menu to access*/
3753 parentMenu
= grab_menu_ptr(menu
->hSysMenuOwner
);
3754 release_menu_ptr(menu
);
3758 hwnd
= parentMenu
->hWnd
;
3759 release_menu_ptr(parentMenu
);
3761 /* Refresh the frame to reflect the change */
3762 WIN_GetRectangles( hwnd
, COORDS_CLIENT
, &rc
, NULL
);
3764 RedrawWindow(hwnd
, &rc
, 0, RDW_FRAME
| RDW_INVALIDATE
| RDW_NOCHILDREN
);
3768 release_menu_ptr(menu
);
3774 /*******************************************************************
3775 * GetMenuStringA (USER32.@)
3777 INT WINAPI
GetMenuStringA(
3778 HMENU hMenu
, /* [in] menuhandle */
3779 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3780 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3781 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3782 UINT wFlags
/* [in] MF_ flags */
3790 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3791 if (str
&& nMaxSiz
) str
[0] = '\0';
3793 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3795 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3798 item
= &menu
->items
[pos
];
3802 else if (!str
|| !nMaxSiz
)
3803 ret
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
, 0, NULL
, NULL
);
3806 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3810 release_menu_ptr(menu
);
3812 TRACE("returning %s\n", debugstr_a(str
));
3817 /*******************************************************************
3818 * GetMenuStringW (USER32.@)
3820 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3821 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3828 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3829 if (str
&& nMaxSiz
) str
[0] = '\0';
3831 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3833 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
3836 item
= &menu
->items
[pos
];
3838 if (!str
|| !nMaxSiz
)
3839 ret
= item
->text
? strlenW(item
->text
) : 0;
3840 else if (!item
->text
)
3847 lstrcpynW( str
, item
->text
, nMaxSiz
);
3850 release_menu_ptr(menu
);
3852 TRACE("returning %s\n", debugstr_w(str
));
3857 /**********************************************************************
3858 * HiliteMenuItem (USER32.@)
3860 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3866 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3868 if (!(menu
= find_menu_item(hMenu
, wItemID
, wHilite
, &pos
))) return FALSE
;
3870 if (menu
->FocusedItem
!= pos
)
3872 MENU_HideSubPopups( hWnd
, menu
->obj
.handle
, FALSE
, 0 );
3873 MENU_SelectItem( hWnd
, menu
->obj
.handle
, pos
, TRUE
, 0 );
3875 release_menu_ptr(menu
);
3880 /**********************************************************************
3881 * GetMenuState (USER32.@)
3883 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3889 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3891 if (!(menu
= find_menu_item(hMenu
, wItemID
, wFlags
, &pos
)))
3894 item
= &menu
->items
[pos
];
3895 debug_print_menuitem (" item: ", item
, "");
3896 if (item
->fType
& MF_POPUP
)
3898 POPUPMENU
*submenu
= grab_menu_ptr(item
->hSubMenu
);
3900 state
= (submenu
->nItems
<< 8) | ((item
->fState
| item
->fType
) & 0xff);
3903 release_menu_ptr(submenu
);
3907 /* We used to (from way back then) mask the result to 0xff. */
3908 /* I don't know why and it seems wrong as the documented */
3909 /* return flag MF_SEPARATOR is outside that mask. */
3910 state
= (item
->fType
| item
->fState
);
3912 release_menu_ptr(menu
);
3917 /**********************************************************************
3918 * GetMenuItemCount (USER32.@)
3920 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3922 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
3925 if (!menu
) return -1;
3926 count
= menu
->nItems
;
3927 release_menu_ptr(menu
);
3929 TRACE("(%p) returning %d\n", hMenu
, count
);
3934 /**********************************************************************
3935 * GetMenuItemID (USER32.@)
3937 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3942 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
3945 id
= menu
->items
[pos
].fType
& MF_POPUP
? -1 : menu
->items
[pos
].wID
;
3946 release_menu_ptr(menu
);
3951 /**********************************************************************
3954 * Uses flags, id and text ptr, passed by InsertMenu() and
3955 * ModifyMenu() to setup a MenuItemInfo structure.
3957 static void MENU_mnu2mnuii( UINT flags
, UINT_PTR id
, LPCWSTR str
,
3958 LPMENUITEMINFOW pmii
)
3960 ZeroMemory( pmii
, sizeof( MENUITEMINFOW
));
3961 pmii
->cbSize
= sizeof( MENUITEMINFOW
);
3962 pmii
->fMask
= MIIM_STATE
| MIIM_ID
| MIIM_FTYPE
;
3963 /* setting bitmap clears text and vice versa */
3964 if( IS_STRING_ITEM(flags
)) {
3965 pmii
->fMask
|= MIIM_STRING
| MIIM_BITMAP
;
3967 flags
|= MF_SEPARATOR
;
3968 /* Item beginning with a backspace is a help item */
3969 /* FIXME: wrong place, this is only true in win16 */
3970 else if( *str
== '\b') {
3974 pmii
->dwTypeData
= (LPWSTR
)str
;
3975 } else if( flags
& MFT_BITMAP
){
3976 pmii
->fMask
|= MIIM_BITMAP
| MIIM_STRING
;
3977 pmii
->hbmpItem
= (HBITMAP
)str
;
3979 if( flags
& MF_OWNERDRAW
){
3980 pmii
->fMask
|= MIIM_DATA
;
3981 pmii
->dwItemData
= (ULONG_PTR
) str
;
3983 if( flags
& MF_POPUP
&& MENU_GetMenu((HMENU
)id
)) {
3984 pmii
->fMask
|= MIIM_SUBMENU
;
3985 pmii
->hSubMenu
= (HMENU
)id
;
3987 if( flags
& MF_SEPARATOR
) flags
|= MF_GRAYED
| MF_DISABLED
;
3988 pmii
->fState
= flags
& MENUITEMINFO_STATE_MASK
& ~MFS_DEFAULT
;
3989 pmii
->fType
= flags
& MENUITEMINFO_TYPE_MASK
;
3990 pmii
->wID
= (UINT
)id
;
3994 /*******************************************************************
3995 * InsertMenuW (USER32.@)
3997 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3998 UINT_PTR id
, LPCWSTR str
)
4006 if (IS_STRING_ITEM(flags
) && str
)
4007 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
4008 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4009 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
4010 hMenu
, pos
, flags
, id
, str
);
4012 if (!(menu
= insert_menu_item(hMenu
, pos
, flags
, &newpos
)))
4015 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4017 item
= &menu
->items
[newpos
];
4018 ret
= SetMenuItemInfo_common( item
, &mii
, TRUE
);
4020 item
->hCheckBit
= item
->hUnCheckBit
= 0;
4022 RemoveMenu( hMenu
, pos
, flags
);
4023 release_menu_ptr(menu
);
4029 /*******************************************************************
4030 * InsertMenuA (USER32.@)
4032 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4033 UINT_PTR id
, LPCSTR str
)
4037 if (IS_STRING_ITEM(flags
) && str
)
4039 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4040 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4043 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4044 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
4045 HeapFree( GetProcessHeap(), 0, newstr
);
4049 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4053 /*******************************************************************
4054 * AppendMenuA (USER32.@)
4056 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
4057 UINT_PTR id
, LPCSTR data
)
4059 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4063 /*******************************************************************
4064 * AppendMenuW (USER32.@)
4066 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
4067 UINT_PTR id
, LPCWSTR data
)
4069 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
4073 /**********************************************************************
4074 * RemoveMenu (USER32.@)
4076 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT id
, UINT flags
)
4081 TRACE("(menu=%p id=%#x flags=%04x)\n", hMenu
, id
, flags
);
4083 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4087 MENU_FreeItemData( &menu
->items
[pos
] );
4089 if (--menu
->nItems
== 0)
4091 HeapFree( GetProcessHeap(), 0, menu
->items
);
4096 MENUITEM
*new_items
, *item
= &menu
->items
[pos
];
4098 while (pos
< menu
->nItems
)
4104 new_items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
, menu
->nItems
* sizeof(MENUITEM
) );
4105 if (new_items
) menu
->items
= new_items
;
4107 release_menu_ptr(menu
);
4113 /**********************************************************************
4114 * DeleteMenu (USER32.@)
4116 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT id
, UINT flags
)
4121 if (!(menu
= find_menu_item(hMenu
, id
, flags
, &pos
)))
4124 if (menu
->items
[pos
].fType
& MF_POPUP
)
4125 DestroyMenu(menu
->items
[pos
].hSubMenu
);
4127 RemoveMenu(menu
->obj
.handle
, pos
, flags
| MF_BYPOSITION
);
4128 release_menu_ptr(menu
);
4133 /*******************************************************************
4134 * ModifyMenuW (USER32.@)
4136 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
4137 UINT_PTR id
, LPCWSTR str
)
4144 if (IS_STRING_ITEM(flags
))
4145 TRACE("%p %d %04x %04lx %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
4147 TRACE("%p %d %04x %04lx %p\n", hMenu
, pos
, flags
, id
, str
);
4149 if (!(menu
= find_menu_item(hMenu
, pos
, flags
, &item_pos
)))
4151 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
4152 if (pos
== SC_TASKLIST
&& !(flags
& MF_BYPOSITION
)) return TRUE
;
4155 menu
->Height
= 0; /* force size recalculate */
4156 MENU_mnu2mnuii( flags
, id
, str
, &mii
);
4157 ret
= SetMenuItemInfo_common(&menu
->items
[item_pos
], &mii
, TRUE
);
4158 release_menu_ptr(menu
);
4163 /*******************************************************************
4164 * ModifyMenuA (USER32.@)
4166 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
4167 UINT_PTR id
, LPCSTR str
)
4171 if (IS_STRING_ITEM(flags
) && str
)
4173 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4174 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
4177 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
4178 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
4179 HeapFree( GetProcessHeap(), 0, newstr
);
4183 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
4187 /**********************************************************************
4188 * CreatePopupMenu (USER32.@)
4190 HMENU WINAPI
CreatePopupMenu(void)
4195 if (!(hmenu
= CreateMenu())) return 0;
4196 menu
= MENU_GetMenu( hmenu
);
4197 menu
->wFlags
|= MF_POPUP
;
4202 /**********************************************************************
4203 * GetMenuCheckMarkDimensions (USER.417)
4204 * GetMenuCheckMarkDimensions (USER32.@)
4206 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
4208 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
4212 /**********************************************************************
4213 * SetMenuItemBitmaps (USER32.@)
4215 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
4216 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
4222 if (!(menu
= find_menu_item(hMenu
, nPos
, wFlags
, &pos
)))
4225 item
= &menu
->items
[pos
];
4226 if (!hNewCheck
&& !hNewUnCheck
)
4228 item
->fState
&= ~MF_USECHECKBITMAPS
;
4230 else /* Install new bitmaps */
4232 item
->hCheckBit
= hNewCheck
;
4233 item
->hUnCheckBit
= hNewUnCheck
;
4234 item
->fState
|= MF_USECHECKBITMAPS
;
4236 release_menu_ptr(menu
);
4242 /**********************************************************************
4243 * CreateMenu (USER32.@)
4245 HMENU WINAPI
CreateMenu(void)
4250 if (!(menu
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*menu
) ))) return 0;
4251 menu
->FocusedItem
= NO_SELECTED_ITEM
;
4254 if (!(hMenu
= alloc_user_handle( &menu
->obj
, USER_MENU
))) HeapFree( GetProcessHeap(), 0, menu
);
4256 TRACE("return %p\n", hMenu
);
4262 /**********************************************************************
4263 * DestroyMenu (USER32.@)
4265 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
4269 TRACE("(%p)\n", hMenu
);
4271 if (!(lppop
= free_user_handle( hMenu
, USER_MENU
))) return FALSE
;
4272 if (lppop
== OBJ_OTHER_PROCESS
) return FALSE
;
4274 /* DestroyMenu should not destroy system menu popup owner */
4275 if ((lppop
->wFlags
& (MF_POPUP
| MF_SYSMENU
)) == MF_POPUP
&& lppop
->hWnd
)
4277 DestroyWindow( lppop
->hWnd
);
4281 if (lppop
->items
) /* recursively destroy submenus */
4284 MENUITEM
*item
= lppop
->items
;
4285 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
4287 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
4288 MENU_FreeItemData( item
);
4290 HeapFree( GetProcessHeap(), 0, lppop
->items
);
4292 HeapFree( GetProcessHeap(), 0, lppop
);
4297 /**********************************************************************
4298 * GetSystemMenu (USER32.@)
4300 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
4302 WND
*wndPtr
= WIN_GetPtr( hWnd
);
4305 if (wndPtr
== WND_DESKTOP
) return 0;
4306 if (wndPtr
== WND_OTHER_PROCESS
)
4308 if (IsWindow( hWnd
)) FIXME( "not supported on other process window %p\n", hWnd
);
4312 if (wndPtr
->hSysMenu
&& bRevert
)
4314 DestroyMenu(wndPtr
->hSysMenu
);
4315 wndPtr
->hSysMenu
= 0;
4318 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
4319 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, 0 );
4321 if( wndPtr
->hSysMenu
)
4324 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
4326 /* Store the dummy sysmenu handle to facilitate the refresh */
4327 /* of the close button if the SC_CLOSE item change */
4328 menu
= MENU_GetMenu(retvalue
);
4330 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
4332 WIN_ReleasePtr( wndPtr
);
4334 return bRevert
? 0 : retvalue
;
4338 /*******************************************************************
4339 * SetSystemMenu (USER32.@)
4341 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
4343 WND
*wndPtr
= WIN_GetPtr( hwnd
);
4345 if (wndPtr
&& wndPtr
!= WND_OTHER_PROCESS
&& wndPtr
!= WND_DESKTOP
)
4347 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
4348 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
4349 WIN_ReleasePtr( wndPtr
);
4356 /**********************************************************************
4357 * GetMenu (USER32.@)
4359 HMENU WINAPI
GetMenu( HWND hWnd
)
4361 HMENU retvalue
= (HMENU
)GetWindowLongPtrW( hWnd
, GWLP_ID
);
4362 TRACE("for %p returning %p\n", hWnd
, retvalue
);
4366 /**********************************************************************
4367 * GetMenuBarInfo (USER32.@)
4369 BOOL WINAPI
GetMenuBarInfo( HWND hwnd
, LONG idObject
, LONG idItem
, PMENUBARINFO pmbi
)
4375 TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd
, idObject
, idItem
, pmbi
);
4380 class_atom
= GetClassLongW(hwnd
, GCW_ATOM
);
4383 if (class_atom
!= POPUPMENU_CLASS_ATOM
)
4385 WARN("called on invalid window: %d\n", class_atom
);
4386 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4390 hmenu
= (HMENU
)GetWindowLongPtrW(hwnd
, 0);
4393 hmenu
= GetMenu(hwnd
);
4396 hmenu
= GetSystemMenu(hwnd
, FALSE
);
4405 if (pmbi
->cbSize
!= sizeof(MENUBARINFO
))
4407 SetLastError(ERROR_INVALID_PARAMETER
);
4411 menu
= MENU_GetMenu(hmenu
);
4414 if (idItem
< 0 || idItem
> menu
->nItems
)
4419 SetRectEmpty(&pmbi
->rcBar
);
4421 else if (idItem
== 0)
4423 GetMenuItemRect(hwnd
, hmenu
, 0, &pmbi
->rcBar
);
4424 pmbi
->rcBar
.right
= pmbi
->rcBar
.left
+ menu
->Width
;
4425 pmbi
->rcBar
.bottom
= pmbi
->rcBar
.top
+ menu
->Height
;
4429 GetMenuItemRect(hwnd
, hmenu
, idItem
- 1, &pmbi
->rcBar
);
4432 pmbi
->hMenu
= hmenu
;
4433 pmbi
->hwndMenu
= NULL
;
4434 pmbi
->fBarFocused
= top_popup_hmenu
== hmenu
;
4437 pmbi
->fFocused
= menu
->FocusedItem
== idItem
- 1;
4438 if (pmbi
->fFocused
&& (menu
->items
[idItem
- 1].fType
& MF_POPUP
))
4440 menu
= MENU_GetMenu(menu
->items
[idItem
- 1].hSubMenu
);
4442 pmbi
->hwndMenu
= menu
->hWnd
;
4447 pmbi
->fFocused
= pmbi
->fBarFocused
;
4453 /**********************************************************************
4456 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4457 * SetWindowPos call that would result if SetMenu were called directly.
4459 BOOL
MENU_SetMenu( HWND hWnd
, HMENU hMenu
)
4461 TRACE("(%p, %p);\n", hWnd
, hMenu
);
4463 if (hMenu
&& !IsMenu(hMenu
))
4465 WARN("hMenu %p is not a menu handle\n", hMenu
);
4468 if (is_win_menu_disallowed(hWnd
))
4471 hWnd
= WIN_GetFullHandle( hWnd
);
4472 if (GetCapture() == hWnd
)
4473 set_capture_window( 0, GUI_INMENUMODE
, NULL
); /* release the capture */
4479 if (!(menu
= grab_menu_ptr(hMenu
))) return FALSE
;
4481 menu
->Height
= 0; /* Make sure we recalculate the size */
4482 release_menu_ptr(menu
);
4484 SetWindowLongPtrW( hWnd
, GWLP_ID
, (LONG_PTR
)hMenu
);
4489 /**********************************************************************
4490 * SetMenu (USER32.@)
4492 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
4494 if(!MENU_SetMenu(hWnd
, hMenu
))
4497 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4498 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4503 /**********************************************************************
4504 * GetSubMenu (USER32.@)
4506 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
4512 if (!(menu
= find_menu_item(hMenu
, nPos
, MF_BYPOSITION
, &pos
)))
4515 if (menu
->items
[pos
].fType
& MF_POPUP
)
4516 submenu
= menu
->items
[pos
].hSubMenu
;
4520 release_menu_ptr(menu
);
4525 /**********************************************************************
4526 * DrawMenuBar (USER32.@)
4528 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
4532 if (!IsWindow( hWnd
))
4534 if (is_win_menu_disallowed(hWnd
))
4537 if ((hMenu
= GetMenu( hWnd
)))
4539 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
4542 menu
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
4543 menu
->hwndOwner
= hWnd
;
4544 release_menu_ptr(menu
);
4548 return SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
4549 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
4552 /***********************************************************************
4553 * DrawMenuBarTemp (USER32.@)
4557 * called by W98SE desk.cpl Control Panel Applet
4559 * Not 100% sure about the param names, but close.
4561 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
4566 BOOL flat_menu
= FALSE
;
4568 SystemParametersInfoW (SPI_GETFLATMENU
, 0, &flat_menu
, 0);
4571 hMenu
= GetMenu(hwnd
);
4574 hFont
= get_menu_font(FALSE
);
4576 lppop
= MENU_GetMenu( hMenu
);
4577 if (lppop
== NULL
|| lprect
== NULL
)
4579 retvalue
= GetSystemMetrics(SM_CYMENU
);
4583 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
4585 hfontOld
= SelectObject( hDC
, hFont
);
4587 if (lppop
->Height
== 0)
4588 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
4590 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
4592 FillRect(hDC
, lprect
, GetSysColorBrush(flat_menu
? COLOR_MENUBAR
: COLOR_MENU
) );
4594 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
4595 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
4596 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
4598 if (lppop
->nItems
== 0)
4600 retvalue
= GetSystemMetrics(SM_CYMENU
);
4604 for (i
= 0; i
< lppop
->nItems
; i
++)
4605 MENU_DrawMenuItem( hwnd
, lppop
, hwnd
, hDC
, &lppop
->items
[i
], TRUE
, ODA_DRAWENTIRE
);
4607 retvalue
= lppop
->Height
;
4610 if (hfontOld
) SelectObject (hDC
, hfontOld
);
4614 /***********************************************************************
4615 * EndMenu (USER.187)
4616 * EndMenu (USER32.@)
4618 BOOL WINAPI
EndMenu(void)
4620 /* if we are in the menu code, and it is active */
4621 if (!fEndMenu
&& top_popup
)
4623 /* terminate the menu handling code */
4626 /* needs to be posted to wakeup the internal menu handler */
4627 /* which will now terminate the menu, in the event that */
4628 /* the main window was minimized, or lost focus, so we */
4629 /* don't end up with an orphaned menu */
4630 PostMessageW( top_popup
, WM_CANCELMODE
, 0, 0);
4636 /*****************************************************************
4637 * LoadMenuA (USER32.@)
4639 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
4641 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
4642 if (!hrsrc
) return 0;
4643 return LoadMenuIndirectA( LoadResource( instance
, hrsrc
));
4647 /*****************************************************************
4648 * LoadMenuW (USER32.@)
4650 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
4652 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
4653 if (!hrsrc
) return 0;
4654 return LoadMenuIndirectW( LoadResource( instance
, hrsrc
));
4658 /**********************************************************************
4659 * LoadMenuIndirectW (USER32.@)
4661 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4664 WORD version
, offset
;
4665 LPCSTR p
= template;
4667 version
= GET_WORD(p
);
4669 TRACE("%p, ver %d\n", template, version
);
4672 case 0: /* standard format is version of 0 */
4673 offset
= GET_WORD(p
);
4674 p
+= sizeof(WORD
) + offset
;
4675 if (!(hMenu
= CreateMenu())) return 0;
4676 if (!MENU_ParseResource( p
, hMenu
))
4678 DestroyMenu( hMenu
);
4682 case 1: /* extended format is version of 1 */
4683 offset
= GET_WORD(p
);
4684 p
+= sizeof(WORD
) + offset
;
4685 if (!(hMenu
= CreateMenu())) return 0;
4686 if (!MENUEX_ParseResource( p
, hMenu
))
4688 DestroyMenu( hMenu
);
4693 ERR("version %d not supported.\n", version
);
4699 /**********************************************************************
4700 * LoadMenuIndirectA (USER32.@)
4702 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4704 return LoadMenuIndirectW( template );
4708 /**********************************************************************
4711 BOOL WINAPI
IsMenu(HMENU hmenu
)
4716 menu
= grab_menu_ptr(hmenu
);
4717 is_menu
= menu
!= NULL
;
4718 release_menu_ptr(menu
);
4721 SetLastError(ERROR_INVALID_MENU_HANDLE
);
4726 /**********************************************************************
4727 * GetMenuItemInfo_common
4730 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT id
, BOOL bypos
,
4731 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4737 menu
= find_menu_item(hmenu
, id
, bypos
? MF_BYPOSITION
: 0, &pos
);
4739 item
= menu
? &menu
->items
[pos
] : NULL
;
4741 debug_print_menuitem("GetMenuItemInfo_common: ", item
, "");
4745 SetLastError( ERROR_MENU_ITEM_NOT_FOUND
);
4749 if( lpmii
->fMask
& MIIM_TYPE
) {
4750 if( lpmii
->fMask
& ( MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) {
4751 release_menu_ptr(menu
);
4752 WARN("invalid combination of fMask bits used\n");
4753 /* this does not happen on Win9x/ME */
4754 SetLastError( ERROR_INVALID_PARAMETER
);
4757 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4758 if (item
->hbmpItem
&& !IS_MAGIC_BITMAP(item
->hbmpItem
))
4759 lpmii
->fType
|= MFT_BITMAP
;
4760 lpmii
->hbmpItem
= item
->hbmpItem
; /* not on Win9x/ME */
4761 if( lpmii
->fType
& MFT_BITMAP
) {
4762 lpmii
->dwTypeData
= (LPWSTR
) item
->hbmpItem
;
4764 } else if( lpmii
->fType
& (MFT_OWNERDRAW
| MFT_SEPARATOR
)) {
4765 /* this does not happen on Win9x/ME */
4766 lpmii
->dwTypeData
= 0;
4771 /* copy the text string */
4772 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
))) {
4774 if(lpmii
->dwTypeData
&& lpmii
->cch
) {
4776 *((WCHAR
*)lpmii
->dwTypeData
) = 0;
4778 *((CHAR
*)lpmii
->dwTypeData
) = 0;
4785 len
= strlenW(item
->text
);
4786 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4787 lstrcpynW(lpmii
->dwTypeData
, item
->text
, lpmii
->cch
);
4791 len
= WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, NULL
,
4792 0, NULL
, NULL
) - 1;
4793 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4794 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1,
4795 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4796 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
- 1] = 0;
4798 /* if we've copied a substring we return its length */
4799 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4800 if (lpmii
->cch
<= len
+ 1)
4805 /* return length of string */
4806 /* not on Win9x/ME if fType & MFT_BITMAP */
4812 if (lpmii
->fMask
& MIIM_FTYPE
)
4813 lpmii
->fType
= item
->fType
& MENUITEMINFO_TYPE_MASK
;
4815 if (lpmii
->fMask
& MIIM_BITMAP
)
4816 lpmii
->hbmpItem
= item
->hbmpItem
;
4818 if (lpmii
->fMask
& MIIM_STATE
)
4819 lpmii
->fState
= item
->fState
& MENUITEMINFO_STATE_MASK
;
4821 if (lpmii
->fMask
& MIIM_ID
)
4822 lpmii
->wID
= item
->wID
;
4824 if (lpmii
->fMask
& MIIM_SUBMENU
)
4825 lpmii
->hSubMenu
= item
->hSubMenu
;
4827 /* hSubMenu is always cleared
4828 * (not on Win9x/ME ) */
4829 lpmii
->hSubMenu
= 0;
4832 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4833 lpmii
->hbmpChecked
= item
->hCheckBit
;
4834 lpmii
->hbmpUnchecked
= item
->hUnCheckBit
;
4836 if (lpmii
->fMask
& MIIM_DATA
)
4837 lpmii
->dwItemData
= item
->dwItemData
;
4839 release_menu_ptr(menu
);
4843 /**********************************************************************
4844 * GetMenuItemInfoA (USER32.@)
4846 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4847 LPMENUITEMINFOA lpmii
)
4851 if( lpmii
->cbSize
!= sizeof( mii
) &&
4852 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4853 SetLastError( ERROR_INVALID_PARAMETER
);
4856 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4857 mii
.cbSize
= sizeof( mii
);
4858 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
,
4859 (LPMENUITEMINFOW
)&mii
, FALSE
);
4860 mii
.cbSize
= lpmii
->cbSize
;
4861 memcpy( lpmii
, &mii
, mii
.cbSize
);
4865 /**********************************************************************
4866 * GetMenuItemInfoW (USER32.@)
4868 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4869 LPMENUITEMINFOW lpmii
)
4873 if( lpmii
->cbSize
!= sizeof( mii
) &&
4874 lpmii
->cbSize
!= sizeof( mii
) - sizeof ( mii
.hbmpItem
)) {
4875 SetLastError( ERROR_INVALID_PARAMETER
);
4878 memcpy( &mii
, lpmii
, lpmii
->cbSize
);
4879 mii
.cbSize
= sizeof( mii
);
4880 ret
= GetMenuItemInfo_common (hmenu
, item
, bypos
, &mii
, TRUE
);
4881 mii
.cbSize
= lpmii
->cbSize
;
4882 memcpy( lpmii
, &mii
, mii
.cbSize
);
4887 /* set a menu item text from a ASCII or Unicode string */
4888 static inline void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4894 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4895 strcpyW( menu
->text
, text
);
4899 LPCSTR str
= (LPCSTR
)text
;
4900 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4901 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4902 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4907 /**********************************************************************
4910 * detect if there are loops in the menu tree (or the depth is too large)
4912 static int MENU_depth( POPUPMENU
*pmenu
, int depth
)
4919 if( depth
> MAXMENUDEPTH
) return depth
;
4920 item
= pmenu
->items
;
4922 for( i
= 0; i
< pmenu
->nItems
&& subdepth
<= MAXMENUDEPTH
; i
++, item
++){
4923 POPUPMENU
*psubmenu
= item
->hSubMenu
? MENU_GetMenu( item
->hSubMenu
) : NULL
;
4925 int bdepth
= MENU_depth( psubmenu
, depth
);
4926 if( bdepth
> subdepth
) subdepth
= bdepth
;
4928 if( subdepth
> MAXMENUDEPTH
)
4929 TRACE("<- hmenu %p\n", item
->hSubMenu
);
4935 /**********************************************************************
4936 * SetMenuItemInfo_common
4938 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4939 * MIIM_BITMAP and MIIM_STRING flags instead.
4942 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4943 const MENUITEMINFOW
*lpmii
,
4946 if (!menu
) return FALSE
;
4948 debug_print_menuitem("SetMenuItemInfo_common from: ", menu
, "");
4950 if (lpmii
->fMask
& MIIM_FTYPE
) {
4951 menu
->fType
&= ~MENUITEMINFO_TYPE_MASK
;
4952 menu
->fType
|= lpmii
->fType
& MENUITEMINFO_TYPE_MASK
;
4954 if (lpmii
->fMask
& MIIM_STRING
) {
4955 /* free the string when used */
4956 HeapFree(GetProcessHeap(), 0, menu
->text
);
4957 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4960 if (lpmii
->fMask
& MIIM_STATE
)
4961 /* Other menu items having MFS_DEFAULT are not converted
4963 menu
->fState
= lpmii
->fState
& MENUITEMINFO_STATE_MASK
;
4965 if (lpmii
->fMask
& MIIM_ID
)
4966 menu
->wID
= lpmii
->wID
;
4968 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4969 menu
->hSubMenu
= lpmii
->hSubMenu
;
4970 if (menu
->hSubMenu
) {
4971 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4973 if( MENU_depth( subMenu
, 0) > MAXMENUDEPTH
) {
4974 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4978 subMenu
->wFlags
|= MF_POPUP
;
4979 menu
->fType
|= MF_POPUP
;
4981 SetLastError( ERROR_INVALID_PARAMETER
);
4986 menu
->fType
&= ~MF_POPUP
;
4989 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4991 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4992 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4994 if (lpmii
->fMask
& MIIM_DATA
)
4995 menu
->dwItemData
= lpmii
->dwItemData
;
4997 if (lpmii
->fMask
& MIIM_BITMAP
)
4998 menu
->hbmpItem
= lpmii
->hbmpItem
;
5000 if( !menu
->text
&& !(menu
->fType
& MFT_OWNERDRAW
) && !menu
->hbmpItem
)
5001 menu
->fType
|= MFT_SEPARATOR
;
5003 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
5007 /**********************************************************************
5008 * MENU_NormalizeMenuItemInfoStruct
5010 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
5011 * check, copy and extend the MENUITEMINFO struct from the version that the application
5012 * supplied to the version used by wine source. */
5013 static BOOL
MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW
*pmii_in
,
5014 MENUITEMINFOW
*pmii_out
)
5016 /* do we recognize the size? */
5017 if( !pmii_in
|| (pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) &&
5018 pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
) - sizeof( pmii_in
->hbmpItem
)) ) {
5019 SetLastError( ERROR_INVALID_PARAMETER
);
5022 /* copy the fields that we have */
5023 memcpy( pmii_out
, pmii_in
, pmii_in
->cbSize
);
5024 /* if the hbmpItem member is missing then extend */
5025 if( pmii_in
->cbSize
!= sizeof( MENUITEMINFOW
)) {
5026 pmii_out
->cbSize
= sizeof( MENUITEMINFOW
);
5027 pmii_out
->hbmpItem
= NULL
;
5029 /* test for invalid bit combinations */
5030 if( (pmii_out
->fMask
& MIIM_TYPE
&&
5031 pmii_out
->fMask
& (MIIM_STRING
| MIIM_FTYPE
| MIIM_BITMAP
)) ||
5032 (pmii_out
->fMask
& MIIM_FTYPE
&& pmii_out
->fType
& MFT_BITMAP
)) {
5033 WARN("invalid combination of fMask bits used\n");
5034 /* this does not happen on Win9x/ME */
5035 SetLastError( ERROR_INVALID_PARAMETER
);
5038 /* convert old style (MIIM_TYPE) to the new */
5039 if( pmii_out
->fMask
& MIIM_TYPE
){
5040 pmii_out
->fMask
|= MIIM_FTYPE
;
5041 if( IS_STRING_ITEM(pmii_out
->fType
)){
5042 pmii_out
->fMask
|= MIIM_STRING
;
5043 } else if( (pmii_out
->fType
) & MFT_BITMAP
){
5044 pmii_out
->fMask
|= MIIM_BITMAP
;
5045 pmii_out
->hbmpItem
= UlongToHandle(LOWORD(pmii_out
->dwTypeData
));
5051 /**********************************************************************
5052 * SetMenuItemInfoA (USER32.@)
5054 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
5055 const MENUITEMINFOA
*lpmii
)
5062 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5064 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5066 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5068 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5069 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5072 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5073 release_menu_ptr(menu
);
5077 /**********************************************************************
5078 * SetMenuItemInfoW (USER32.@)
5080 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
5081 const MENUITEMINFOW
*lpmii
)
5088 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu
, item
, bypos
, lpmii
);
5090 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5092 if (!(menu
= find_menu_item(hmenu
, item
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5094 /* workaround for Word 95: pretend that SC_TASKLIST item exists */
5095 if (item
== SC_TASKLIST
&& !bypos
) return TRUE
;
5099 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5100 release_menu_ptr(menu
);
5104 static BOOL
set_menu_default_item(POPUPMENU
*menu
, UINT uItem
, UINT bypos
)
5109 /* reset all default-item flags */
5111 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5113 item
->fState
&= ~MFS_DEFAULT
;
5116 /* no default item */
5123 if ( uItem
>= menu
->nItems
) return FALSE
;
5124 item
[uItem
].fState
|= MFS_DEFAULT
;
5129 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
5131 if (item
->wID
== uItem
)
5133 item
->fState
|= MFS_DEFAULT
;
5142 /**********************************************************************
5143 * SetMenuDefaultItem (USER32.@)
5146 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
5151 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
5153 if (!(menu
= grab_menu_ptr(hmenu
))) return FALSE
;
5154 ret
= set_menu_default_item(menu
, uItem
, bypos
);
5155 release_menu_ptr(menu
);
5160 /**********************************************************************
5161 * GetMenuDefaultItem (USER32.@)
5163 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
5169 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
5171 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
5173 /* find default item */
5177 if (! item
) return -1;
5179 while ( !( item
->fState
& MFS_DEFAULT
) )
5182 if (i
>= menu
->nItems
) return -1;
5185 /* default: don't return disabled items */
5186 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
5188 /* search rekursiv when needed */
5189 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
5192 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
5193 if ( -1 != ret
) return ret
;
5195 /* when item not found in submenu, return the popup item */
5197 return ( bypos
) ? i
: item
->wID
;
5202 /**********************************************************************
5203 * InsertMenuItemA (USER32.@)
5205 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5206 const MENUITEMINFOA
*lpmii
)
5213 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5215 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW
*)lpmii
, &mii
)) return FALSE
;
5217 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5220 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, FALSE
);
5221 release_menu_ptr(menu
);
5226 /**********************************************************************
5227 * InsertMenuItemW (USER32.@)
5229 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
5230 const MENUITEMINFOW
*lpmii
)
5237 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu
, uItem
, bypos
, lpmii
);
5239 if (!MENU_NormalizeMenuItemInfoStruct( lpmii
, &mii
)) return FALSE
;
5241 if (!(menu
= insert_menu_item(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0, &pos
)))
5244 ret
= SetMenuItemInfo_common(&menu
->items
[pos
], &mii
, TRUE
);
5245 release_menu_ptr(menu
);
5249 /**********************************************************************
5250 * CheckMenuRadioItem (USER32.@)
5253 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
, UINT first
, UINT last
,
5254 UINT check
, UINT flags
)
5256 POPUPMENU
*first_menu
= NULL
, *check_menu
;
5260 for (i
= first
; i
<= last
; i
++)
5264 if (!(check_menu
= find_menu_item(hMenu
, i
, flags
, &check_pos
)))
5268 first_menu
= grab_menu_ptr(check_menu
->obj
.handle
);
5270 if (first_menu
!= check_menu
)
5272 release_menu_ptr(check_menu
);
5276 item
= &check_menu
->items
[check_pos
];
5277 if (item
->fType
!= MFT_SEPARATOR
)
5281 item
->fType
|= MFT_RADIOCHECK
;
5282 item
->fState
|= MFS_CHECKED
;
5287 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
5288 item
->fState
&= ~MFS_CHECKED
;
5292 release_menu_ptr(check_menu
);
5294 release_menu_ptr(first_menu
);
5300 /**********************************************************************
5301 * GetMenuItemRect (USER32.@)
5303 * ATTENTION: Here, the returned values in rect are the screen
5304 * coordinates of the item just like if the menu was
5305 * always on the upper left side of the application.
5308 BOOL WINAPI
GetMenuItemRect(HWND hwnd
, HMENU hMenu
, UINT uItem
, RECT
*rect
)
5313 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
5318 if (!(menu
= find_menu_item(hMenu
, uItem
, MF_BYPOSITION
, &pos
)))
5321 if (!hwnd
) hwnd
= menu
->hWnd
;
5324 release_menu_ptr(menu
);
5328 *rect
= menu
->items
[pos
].rect
;
5329 OffsetRect(rect
, menu
->items_rect
.left
, menu
->items_rect
.top
);
5330 release_menu_ptr(menu
);
5332 MapWindowPoints(hwnd
, 0, (POINT
*)rect
, 2);
5336 /**********************************************************************
5337 * SetMenuInfo (USER32.@)
5340 * actually use the items to draw the menu
5341 * (recalculate and/or redraw)
5343 static BOOL
menu_SetMenuInfo( HMENU hMenu
, LPCMENUINFO lpmi
)
5346 if( !(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
5348 if (lpmi
->fMask
& MIM_BACKGROUND
)
5349 menu
->hbrBack
= lpmi
->hbrBack
;
5351 if (lpmi
->fMask
& MIM_HELPID
)
5352 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
5354 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5355 menu
->cyMax
= lpmi
->cyMax
;
5357 if (lpmi
->fMask
& MIM_MENUDATA
)
5358 menu
->dwMenuData
= lpmi
->dwMenuData
;
5360 if (lpmi
->fMask
& MIM_STYLE
)
5361 menu
->dwStyle
= lpmi
->dwStyle
;
5363 if( lpmi
->fMask
& MIM_APPLYTOSUBMENUS
) {
5365 MENUITEM
*item
= menu
->items
;
5366 for( i
= menu
->nItems
; i
; i
--, item
++)
5367 if( item
->fType
& MF_POPUP
)
5368 menu_SetMenuInfo( item
->hSubMenu
, lpmi
);
5373 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
5375 TRACE("(%p %p)\n", hMenu
, lpmi
);
5376 if( lpmi
&& (lpmi
->cbSize
== sizeof( MENUINFO
)) && (menu_SetMenuInfo( hMenu
, lpmi
))) {
5377 if( lpmi
->fMask
& MIM_STYLE
) {
5378 if (lpmi
->dwStyle
& MNS_AUTODISMISS
) FIXME("MNS_AUTODISMISS unimplemented\n");
5379 if (lpmi
->dwStyle
& MNS_DRAGDROP
) FIXME("MNS_DRAGDROP unimplemented\n");
5380 if (lpmi
->dwStyle
& MNS_MODELESS
) FIXME("MNS_MODELESS unimplemented\n");
5384 SetLastError( ERROR_INVALID_PARAMETER
);
5388 /**********************************************************************
5389 * GetMenuInfo (USER32.@)
5395 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
5399 TRACE("(%p %p)\n", hMenu
, lpmi
);
5401 if (lpmi
&& (lpmi
->cbSize
== sizeof(MENUINFO
)) && (menu
= grab_menu_ptr(hMenu
)))
5403 if (lpmi
->fMask
& MIM_BACKGROUND
)
5404 lpmi
->hbrBack
= menu
->hbrBack
;
5406 if (lpmi
->fMask
& MIM_HELPID
)
5407 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
5409 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
5410 lpmi
->cyMax
= menu
->cyMax
;
5412 if (lpmi
->fMask
& MIM_MENUDATA
)
5413 lpmi
->dwMenuData
= menu
->dwMenuData
;
5415 if (lpmi
->fMask
& MIM_STYLE
)
5416 lpmi
->dwStyle
= menu
->dwStyle
;
5418 release_menu_ptr(menu
);
5421 SetLastError( ERROR_INVALID_PARAMETER
);
5426 /**********************************************************************
5427 * SetMenuContextHelpId (USER32.@)
5429 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
5433 TRACE("(%p 0x%08x)\n", hMenu
, dwContextHelpID
);
5435 if ((menu
= grab_menu_ptr(hMenu
)))
5437 menu
->dwContextHelpID
= dwContextHelpID
;
5438 release_menu_ptr(menu
);
5445 /**********************************************************************
5446 * GetMenuContextHelpId (USER32.@)
5448 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
5453 TRACE("(%p)\n", hMenu
);
5455 if ((menu
= grab_menu_ptr(hMenu
)))
5457 help_id
= menu
->dwContextHelpID
;
5458 release_menu_ptr(menu
);
5464 /**********************************************************************
5465 * MenuItemFromPoint (USER32.@)
5467 INT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
5469 POPUPMENU
*menu
= grab_menu_ptr(hMenu
);
5472 /*FIXME: Do we have to handle hWnd here? */
5473 if (!menu
) return -1;
5475 if (MENU_FindItemByCoords( menu
, ptScreen
, &pos
) != ht_item
)
5478 release_menu_ptr(menu
);
5483 /**********************************************************************
5484 * CalcMenuBar (USER32.@)
5486 DWORD WINAPI
CalcMenuBar(HWND hwnd
, DWORD left
, DWORD right
, DWORD top
, RECT
*rect
)
5488 FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd
, left
, right
, top
, rect
);
5493 /**********************************************************************
5494 * translate_accelerator
5496 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
5497 BYTE fVirt
, WORD key
, WORD cmd
)
5502 if (wParam
!= key
) return FALSE
;
5504 if (GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
5505 if (GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
5506 if (GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
5508 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
5510 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
5512 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
5518 if(fVirt
& FVIRTKEY
)
5520 TRACE_(accel
)("found accel for virt_key %04lx (scan %04x)\n",
5521 wParam
, 0xff & HIWORD(lParam
));
5523 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
5524 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
5528 if (!(lParam
& 0x01000000)) /* no special_key */
5530 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
5531 { /* ^^ ALT pressed */
5532 TRACE_(accel
)("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
5541 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
5545 HMENU hMenu
, hSubMenu
, hSysMenu
;
5546 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
5549 hMenu
= (GetWindowLongW( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
5550 hSysMenu
= get_win_sys_menu( hWnd
);
5552 /* find menu item and ask application to initialize it */
5553 /* 1. in the system menu */
5554 if ((menu
= find_menu_item(hSysMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5556 hSubMenu
= menu
->obj
.handle
;
5557 release_menu_ptr(menu
);
5561 if (!IsWindowEnabled(hWnd
))
5565 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
5566 if(hSubMenu
!= hSysMenu
)
5568 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
5569 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
5570 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
5572 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
5575 else /* 2. in the window's menu */
5577 if ((menu
= find_menu_item(hMenu
, cmd
, MF_BYCOMMAND
, NULL
)))
5579 hSubMenu
= menu
->obj
.handle
;
5580 release_menu_ptr(menu
);
5584 if (!IsWindowEnabled(hWnd
))
5588 SendMessageW(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
5589 if(hSubMenu
!= hMenu
)
5591 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
5592 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
5593 SendMessageW(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
5595 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
5602 if (uSysStat
!= (UINT
)-1)
5604 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
5611 if (uStat
!= (UINT
)-1)
5617 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
5629 if( mesg
==WM_COMMAND
)
5631 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
5632 SendMessageW(hWnd
, mesg
, 0x10000 | cmd
, 0L);
5634 else if( mesg
==WM_SYSCOMMAND
)
5636 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
5637 SendMessageW(hWnd
, mesg
, cmd
, 0x00010000L
);
5641 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5642 * #0: unknown (please report!)
5643 * #1: for WM_KEYUP,WM_SYSKEYUP
5644 * #2: mouse is captured
5645 * #3: window is disabled
5646 * #4: it's a disabled system menu option
5647 * #5: it's a menu option, but window is iconic
5648 * #6: it's a menu option, but disabled
5650 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
5652 ERR_(accel
)(" unknown reason - please report!\n");
5657 /**********************************************************************
5658 * TranslateAcceleratorA (USER32.@)
5659 * TranslateAccelerator (USER32.@)
5661 INT WINAPI
TranslateAcceleratorA( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5663 switch (msg
->message
)
5667 return TranslateAcceleratorW( hWnd
, hAccel
, msg
);
5673 char ch
= LOWORD(msg
->wParam
);
5675 MultiByteToWideChar(CP_ACP
, 0, &ch
, 1, &wch
, 1);
5676 msgW
.wParam
= MAKEWPARAM(wch
, HIWORD(msg
->wParam
));
5677 return TranslateAcceleratorW( hWnd
, hAccel
, &msgW
);
5685 /**********************************************************************
5686 * TranslateAcceleratorW (USER32.@)
5688 INT WINAPI
TranslateAcceleratorW( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
5690 ACCEL data
[32], *ptr
= data
;
5693 if (!hWnd
) return 0;
5695 if (msg
->message
!= WM_KEYDOWN
&&
5696 msg
->message
!= WM_SYSKEYDOWN
&&
5697 msg
->message
!= WM_CHAR
&&
5698 msg
->message
!= WM_SYSCHAR
)
5701 TRACE_(accel
)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5702 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
5704 if (!(count
= CopyAcceleratorTableW( hAccel
, NULL
, 0 ))) return 0;
5705 if (count
> ARRAY_SIZE( data
))
5707 if (!(ptr
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(*ptr
) ))) return 0;
5709 count
= CopyAcceleratorTableW( hAccel
, ptr
, count
);
5710 for (i
= 0; i
< count
; i
++)
5712 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
5713 ptr
[i
].fVirt
, ptr
[i
].key
, ptr
[i
].cmd
))
5716 if (ptr
!= data
) HeapFree( GetProcessHeap(), 0, ptr
);