4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Note: the style MF_MOUSESELECT is used to mark popup items that
25 * have been selected, i.e. their popup menu is currently displayed.
26 * This is probably not the meaning this style has in MS-Windows.
30 #include "wine/port.h"
42 #include "wine/winbase16.h"
43 #include "wine/winuser16.h"
45 #include "wine/server.h"
46 #include "wine/unicode.h"
49 #include "nonclient.h"
51 #include "wine/debug.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(menu
);
54 WINE_DECLARE_DEBUG_CHANNEL(accel
);
56 /* internal popup menu window messages */
58 #define MM_SETMENUHANDLE (WM_USER + 0)
59 #define MM_GETMENUHANDLE (WM_USER + 1)
61 /* Menu item structure */
63 /* ----------- MENUITEMINFO Stuff ----------- */
64 UINT fType
; /* Item type. */
65 UINT fState
; /* Item state. */
66 UINT_PTR wID
; /* Item id. */
67 HMENU hSubMenu
; /* Pop-up menu. */
68 HBITMAP hCheckBit
; /* Bitmap when checked. */
69 HBITMAP hUnCheckBit
; /* Bitmap when unchecked. */
70 LPWSTR text
; /* Item text or bitmap handle. */
71 DWORD dwItemData
; /* Application defined. */
72 DWORD dwTypeData
; /* depends on fMask */
73 HBITMAP hbmpItem
; /* bitmap in win98 style menus */
74 /* ----------- Wine stuff ----------- */
75 RECT rect
; /* Item area (relative to menu window) */
76 UINT xTab
; /* X position of text after Tab */
79 /* Popup menu structure */
81 WORD wFlags
; /* Menu flags (MF_POPUP, MF_SYSMENU) */
82 WORD wMagic
; /* Magic number */
83 WORD Width
; /* Width of the whole menu */
84 WORD Height
; /* Height of the whole menu */
85 UINT nItems
; /* Number of items in the menu */
86 HWND hWnd
; /* Window containing the menu */
87 MENUITEM
*items
; /* Array of menu items */
88 UINT FocusedItem
; /* Currently focused item */
89 HWND hwndOwner
; /* window receiving the messages for ownerdraw */
90 BOOL bTimeToHide
; /* Request hiding when receiving a second click in the top-level menu item */
91 /* ------------ MENUINFO members ------ */
92 DWORD dwStyle
; /* Extended mennu style */
93 UINT cyMax
; /* max hight of the whole menu, 0 is screen hight */
94 HBRUSH hbrBack
; /* brush for menu background */
95 DWORD dwContextHelpID
;
96 DWORD dwMenuData
; /* application defined value */
97 HMENU hSysMenuOwner
; /* Handle to the dummy sys menu holder */
98 } POPUPMENU
, *LPPOPUPMENU
;
100 /* internal flags for menu tracking */
102 #define TF_ENDMENU 0x0001
103 #define TF_SUSPENDPOPUP 0x0002
104 #define TF_SKIPREMOVE 0x0004
109 HMENU hCurrentMenu
; /* current submenu (can be equal to hTopMenu)*/
110 HMENU hTopMenu
; /* initial menu */
111 HWND hOwnerWnd
; /* where notifications are sent */
115 #define MENU_MAGIC 0x554d /* 'MU' */
120 /* Internal MENU_TrackMenu() flags */
121 #define TPM_INTERNAL 0xF0000000
122 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
123 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
124 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
126 /* popup menu shade thickness */
127 #define POPUP_XSHADE 4
128 #define POPUP_YSHADE 4
130 /* Space between 2 menu bar items */
131 #define MENU_BAR_ITEMS_SPACE 12
133 /* Minimum width of a tab character */
134 #define MENU_TAB_SPACE 8
136 /* Height of a separator item */
137 #define SEPARATOR_HEIGHT 5
139 /* (other menu->FocusedItem values give the position of the focused item) */
140 #define NO_SELECTED_ITEM 0xffff
142 #define MENU_ITEM_TYPE(flags) \
143 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
145 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
146 #define IS_BITMAP_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_BITMAP)
147 #define IS_MAGIC_ITEM(text) (LOWORD((int)text)<12)
149 #define IS_SYSTEM_MENU(menu) \
150 (!((menu)->wFlags & MF_POPUP) && (menu)->wFlags & MF_SYSMENU)
152 #define IS_SYSTEM_POPUP(menu) \
153 ((menu)->wFlags & MF_POPUP && (menu)->wFlags & MF_SYSMENU)
155 #define TYPE_MASK (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
156 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
157 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY | \
158 MF_POPUP | MF_SYSMENU | MF_HELP)
159 #define STATE_MASK (~TYPE_MASK)
161 /* Dimension of the menu bitmaps */
162 static WORD arrow_bitmap_width
= 0, arrow_bitmap_height
= 0;
164 static HBITMAP hStdMnArrow
= 0;
165 static HBITMAP hBmpSysMenu
= 0;
167 static HBRUSH hShadeBrush
= 0;
168 static HFONT hMenuFont
= 0;
169 static HFONT hMenuFontBold
= 0;
171 static HMENU MENU_DefSysPopup
= 0; /* Default system menu popup */
173 /* Use global popup window because there's no way 2 menus can
174 * be tracked at the same time. */
175 static HWND top_popup
;
177 /* Flag set by EndMenu() to force an exit from menu tracking */
178 static BOOL fEndMenu
= FALSE
;
180 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
);
182 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
);
184 /*********************************************************************
185 * menu class descriptor
187 const struct builtin_class_descr MENU_builtin_class
=
189 POPUPMENU_CLASS_ATOM
, /* name */
190 CS_GLOBALCLASS
| CS_SAVEBITS
, /* style */
191 NULL
, /* procA (winproc is Unicode only) */
192 PopupMenuWndProc
, /* procW */
193 sizeof(HMENU
), /* extra */
194 IDC_ARROW
, /* cursor */
195 (HBRUSH
)(COLOR_MENU
+1) /* brush */
199 /***********************************************************************
200 * debug_print_menuitem
202 * Print a menuitem in readable form.
205 #define debug_print_menuitem(pre, mp, post) \
206 if(!TRACE_ON(menu)) ; else do_debug_print_menuitem(pre, mp, post)
208 #define MENUOUT(text) \
209 DPRINTF("%s%s", (count++ ? "," : ""), (text))
211 #define MENUFLAG(bit,text) \
213 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
216 static void do_debug_print_menuitem(const char *prefix
, MENUITEM
* mp
,
219 TRACE("%s ", prefix
);
221 UINT flags
= mp
->fType
;
222 int type
= MENU_ITEM_TYPE(flags
);
223 DPRINTF( "{ ID=0x%x", mp
->wID
);
224 if (flags
& MF_POPUP
)
225 DPRINTF( ", Sub=%p", mp
->hSubMenu
);
229 if (type
== MFT_STRING
)
231 else if (type
== MFT_SEPARATOR
)
233 else if (type
== MFT_OWNERDRAW
)
235 else if (type
== MFT_BITMAP
)
241 MENUFLAG(MF_POPUP
, "pop");
242 MENUFLAG(MFT_MENUBARBREAK
, "barbrk");
243 MENUFLAG(MFT_MENUBREAK
, "brk");
244 MENUFLAG(MFT_RADIOCHECK
, "radio");
245 MENUFLAG(MFT_RIGHTORDER
, "rorder");
246 MENUFLAG(MF_SYSMENU
, "sys");
247 MENUFLAG(MFT_RIGHTJUSTIFY
, "right"); /* same as MF_HELP */
250 DPRINTF( "+0x%x", flags
);
255 DPRINTF( ", State=");
256 MENUFLAG(MFS_GRAYED
, "grey");
257 MENUFLAG(MFS_DEFAULT
, "default");
258 MENUFLAG(MFS_DISABLED
, "dis");
259 MENUFLAG(MFS_CHECKED
, "check");
260 MENUFLAG(MFS_HILITE
, "hi");
261 MENUFLAG(MF_USECHECKBITMAPS
, "usebit");
262 MENUFLAG(MF_MOUSESELECT
, "mouse");
264 DPRINTF( "+0x%x", flags
);
267 DPRINTF( ", Chk=%p", mp
->hCheckBit
);
269 DPRINTF( ", Unc=%p", mp
->hUnCheckBit
);
271 if (type
== MFT_STRING
) {
273 DPRINTF( ", Text=%s", debugstr_w(mp
->text
));
275 DPRINTF( ", Text=Null");
276 } else if (mp
->text
== NULL
)
279 DPRINTF( ", Text=%p", mp
->text
);
281 DPRINTF( ", ItemData=0x%08lx", mp
->dwItemData
);
287 DPRINTF(" %s\n", postfix
);
294 /***********************************************************************
297 * Validate the given menu handle and returns the menu structure pointer.
299 static POPUPMENU
*MENU_GetMenu(HMENU hMenu
)
301 POPUPMENU
*menu
= USER_HEAP_LIN_ADDR(hMenu
);
302 if (!menu
|| menu
->wMagic
!= MENU_MAGIC
)
304 WARN("invalid menu handle=%p, ptr=%p, magic=%x\n", hMenu
, menu
, menu
? menu
->wMagic
:0);
310 /***********************************************************************
313 * Get the system menu of a window
315 static HMENU
get_win_sys_menu( HWND hwnd
)
318 WND
*win
= WIN_FindWndPtr( hwnd
);
322 WIN_ReleaseWndPtr( win
);
327 /***********************************************************************
330 * Return the default system menu.
332 static HMENU
MENU_CopySysPopup(void)
334 HMENU hMenu
= LoadMenuA(GetModuleHandleA("USER32"), "SYSMENU");
337 POPUPMENU
* menu
= MENU_GetMenu(hMenu
);
338 menu
->wFlags
|= MF_SYSMENU
| MF_POPUP
;
339 SetMenuDefaultItem(hMenu
, SC_CLOSE
, FALSE
);
342 ERR("Unable to load default system menu\n" );
344 TRACE("returning %p.\n", hMenu
);
350 /**********************************************************************
353 * Create a copy of the system menu. System menu in Windows is
354 * a special menu bar with the single entry - system menu popup.
355 * This popup is presented to the outside world as a "system menu".
356 * However, the real system menu handle is sometimes seen in the
357 * WM_MENUSELECT parameters (and Word 6 likes it this way).
359 HMENU
MENU_GetSysMenu( HWND hWnd
, HMENU hPopupMenu
)
363 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd
, hPopupMenu
);
364 if ((hMenu
= CreateMenu()))
366 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
367 menu
->wFlags
= MF_SYSMENU
;
368 menu
->hWnd
= WIN_GetFullHandle( hWnd
);
369 TRACE("hWnd %p (hMenu %p)\n", menu
->hWnd
, hMenu
);
371 if (hPopupMenu
== (HMENU
)(-1))
372 hPopupMenu
= MENU_CopySysPopup();
373 else if( !hPopupMenu
) hPopupMenu
= MENU_DefSysPopup
;
377 InsertMenuA( hMenu
, -1, MF_SYSMENU
| MF_POPUP
| MF_BYPOSITION
,
378 (UINT_PTR
)hPopupMenu
, NULL
);
380 menu
->items
[0].fType
= MF_SYSMENU
| MF_POPUP
;
381 menu
->items
[0].fState
= 0;
382 if ((menu
= MENU_GetMenu(hPopupMenu
))) menu
->wFlags
|= MF_SYSMENU
;
384 TRACE("hMenu=%p (hPopup %p)\n", hMenu
, hPopupMenu
);
387 DestroyMenu( hMenu
);
389 ERR("failed to load system menu!\n");
394 /***********************************************************************
397 * Menus initialisation.
402 NONCLIENTMETRICSA ncm
;
404 static unsigned char shade_bits
[16] = { 0x55, 0, 0xAA, 0,
409 /* Load menu bitmaps */
410 hStdMnArrow
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW
));
411 /* Load system buttons bitmaps */
412 hBmpSysMenu
= LoadBitmapA(0, MAKEINTRESOURCEA(OBM_CLOSE
));
417 GetObjectA( hStdMnArrow
, sizeof(bm
), &bm
);
418 arrow_bitmap_width
= bm
.bmWidth
;
419 arrow_bitmap_height
= bm
.bmHeight
;
423 if (! (hBitmap
= CreateBitmap( 8, 8, 1, 1, shade_bits
)))
426 if(!(hShadeBrush
= CreatePatternBrush( hBitmap
)))
429 DeleteObject( hBitmap
);
430 if (!(MENU_DefSysPopup
= MENU_CopySysPopup()))
433 ncm
.cbSize
= sizeof (NONCLIENTMETRICSA
);
434 if (!(SystemParametersInfoA(SPI_GETNONCLIENTMETRICS
, sizeof(NONCLIENTMETRICSA
), &ncm
, 0)))
437 if (!(hMenuFont
= CreateFontIndirectA( &ncm
.lfMenuFont
)))
440 ncm
.lfMenuFont
.lfWeight
+= 300;
441 if ( ncm
.lfMenuFont
.lfWeight
> 1000)
442 ncm
.lfMenuFont
.lfWeight
= 1000;
444 if (!(hMenuFontBold
= CreateFontIndirectA( &ncm
.lfMenuFont
)))
450 /***********************************************************************
451 * MENU_InitSysMenuPopup
453 * Grey the appropriate items in System menu.
455 static void MENU_InitSysMenuPopup( HMENU hmenu
, DWORD style
, DWORD clsStyle
)
459 gray
= !(style
& WS_THICKFRAME
) || (style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
460 EnableMenuItem( hmenu
, SC_SIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
461 gray
= ((style
& WS_MAXIMIZE
) != 0);
462 EnableMenuItem( hmenu
, SC_MOVE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
463 gray
= !(style
& WS_MINIMIZEBOX
) || (style
& WS_MINIMIZE
);
464 EnableMenuItem( hmenu
, SC_MINIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
465 gray
= !(style
& WS_MAXIMIZEBOX
) || (style
& WS_MAXIMIZE
);
466 EnableMenuItem( hmenu
, SC_MAXIMIZE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
467 gray
= !(style
& (WS_MAXIMIZE
| WS_MINIMIZE
));
468 EnableMenuItem( hmenu
, SC_RESTORE
, (gray
? MF_GRAYED
: MF_ENABLED
) );
469 gray
= (clsStyle
& CS_NOCLOSE
) != 0;
471 /* The menu item must keep its state if it's disabled */
473 EnableMenuItem( hmenu
, SC_CLOSE
, MF_GRAYED
);
477 /******************************************************************************
479 * UINT MENU_GetStartOfNextColumn(
482 *****************************************************************************/
484 static UINT
MENU_GetStartOfNextColumn(
487 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
491 return NO_SELECTED_ITEM
;
493 i
= menu
->FocusedItem
+ 1;
494 if( i
== NO_SELECTED_ITEM
)
497 for( ; i
< menu
->nItems
; ++i
) {
498 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
502 return NO_SELECTED_ITEM
;
506 /******************************************************************************
508 * UINT MENU_GetStartOfPrevColumn(
511 *****************************************************************************/
513 static UINT
MENU_GetStartOfPrevColumn(
516 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
520 return NO_SELECTED_ITEM
;
522 if( menu
->FocusedItem
== 0 || menu
->FocusedItem
== NO_SELECTED_ITEM
)
523 return NO_SELECTED_ITEM
;
525 /* Find the start of the column */
527 for(i
= menu
->FocusedItem
; i
!= 0 &&
528 !(menu
->items
[i
].fType
& MF_MENUBARBREAK
);
532 return NO_SELECTED_ITEM
;
534 for(--i
; i
!= 0; --i
) {
535 if (menu
->items
[i
].fType
& MF_MENUBARBREAK
)
539 TRACE("ret %d.\n", i
);
546 /***********************************************************************
549 * Find a menu item. Return a pointer on the item, and modifies *hmenu
550 * in case the item was in a sub-menu.
552 static MENUITEM
*MENU_FindItem( HMENU
*hmenu
, UINT
*nPos
, UINT wFlags
)
557 if ((*hmenu
== (HMENU
)0xffff) || (!(menu
= MENU_GetMenu(*hmenu
)))) return NULL
;
558 if (wFlags
& MF_BYPOSITION
)
560 if (*nPos
>= menu
->nItems
) return NULL
;
561 return &menu
->items
[*nPos
];
565 MENUITEM
*item
= menu
->items
;
566 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
568 if (item
->wID
== *nPos
)
573 else if (item
->fType
& MF_POPUP
)
575 HMENU hsubmenu
= item
->hSubMenu
;
576 MENUITEM
*subitem
= MENU_FindItem( &hsubmenu
, nPos
, wFlags
);
588 /***********************************************************************
591 * Find a Sub menu. Return the position of the submenu, and modifies
592 * *hmenu in case it is found in another sub-menu.
593 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
595 UINT
MENU_FindSubMenu( HMENU
*hmenu
, HMENU hSubTarget
)
600 if (((*hmenu
)==(HMENU
)0xffff) ||
601 (!(menu
= MENU_GetMenu(*hmenu
))))
602 return NO_SELECTED_ITEM
;
604 for (i
= 0; i
< menu
->nItems
; i
++, item
++) {
605 if(!(item
->fType
& MF_POPUP
)) continue;
606 if (item
->hSubMenu
== hSubTarget
) {
610 HMENU hsubmenu
= item
->hSubMenu
;
611 UINT pos
= MENU_FindSubMenu( &hsubmenu
, hSubTarget
);
612 if (pos
!= NO_SELECTED_ITEM
) {
618 return NO_SELECTED_ITEM
;
621 /***********************************************************************
624 static void MENU_FreeItemData( MENUITEM
* item
)
627 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
628 HeapFree( GetProcessHeap(), 0, item
->text
);
631 /***********************************************************************
632 * MENU_FindItemByCoords
634 * Find the item at the specified coordinates (screen coords). Does
635 * not work for child windows and therefore should not be called for
636 * an arbitrary system menu.
638 static MENUITEM
*MENU_FindItemByCoords( POPUPMENU
*menu
,
639 POINT pt
, UINT
*pos
)
645 if (!GetWindowRect(menu
->hWnd
,&wrect
)) return NULL
;
646 pt
.x
-= wrect
.left
;pt
.y
-= wrect
.top
;
648 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
650 if ((pt
.x
>= item
->rect
.left
) && (pt
.x
< item
->rect
.right
) &&
651 (pt
.y
>= item
->rect
.top
) && (pt
.y
< item
->rect
.bottom
))
661 /***********************************************************************
664 * Find the menu item selected by a key press.
665 * Return item id, -1 if none, -2 if we should close the menu.
667 static UINT
MENU_FindItemByKey( HWND hwndOwner
, HMENU hmenu
,
668 UINT key
, BOOL forceMenuChar
)
670 TRACE("\tlooking for '%c' in [%p]\n", (char)key
, hmenu
);
672 if (!IsMenu( hmenu
)) hmenu
= GetSubMenu( get_win_sys_menu(hwndOwner
), 0);
676 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
677 MENUITEM
*item
= menu
->items
;
685 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
687 if (IS_STRING_ITEM(item
->fType
) && item
->text
)
689 WCHAR
*p
= item
->text
- 2;
692 p
= strchrW (p
+ 2, '&');
694 while (p
!= NULL
&& p
[1] == '&');
695 if (p
&& (toupper(p
[1]) == key
)) return i
;
699 menuchar
= SendMessageA( hwndOwner
, WM_MENUCHAR
,
700 MAKEWPARAM( key
, menu
->wFlags
), (LPARAM
)hmenu
);
701 if (HIWORD(menuchar
) == 2) return LOWORD(menuchar
);
702 if (HIWORD(menuchar
) == 1) return (UINT
)(-2);
708 /***********************************************************************
709 * MENU_GetBitmapItemSize
711 * Get the size of a bitmap item.
713 static void MENU_GetBitmapItemSize( UINT id
, DWORD data
, SIZE
*size
)
716 HBITMAP bmp
= (HBITMAP
)id
;
718 size
->cx
= size
->cy
= 0;
720 /* check if there is a magic menu item associated with this item */
721 if (id
&& IS_MAGIC_ITEM( id
))
725 case (INT_PTR
)HBMMENU_SYSTEM
:
732 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
733 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
734 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
735 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
736 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
737 size
->cx
= GetSystemMetrics( SM_CXSIZE
);
738 size
->cy
= GetSystemMetrics( SM_CYSIZE
);
740 case (INT_PTR
)HBMMENU_CALLBACK
:
741 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
742 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
743 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
744 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
746 FIXME("Magic 0x%08x not implemented\n", id
);
750 if (GetObjectA(bmp
, sizeof(bm
), &bm
))
752 size
->cx
= bm
.bmWidth
;
753 size
->cy
= bm
.bmHeight
;
757 /***********************************************************************
758 * MENU_DrawBitmapItem
760 * Draw a bitmap item.
762 static void MENU_DrawBitmapItem( HDC hdc
, MENUITEM
*lpitem
, const RECT
*rect
, BOOL menuBar
)
767 HBITMAP bmp
= (HBITMAP
)lpitem
->text
;
768 int w
= rect
->right
- rect
->left
;
769 int h
= rect
->bottom
- rect
->top
;
773 /* Check if there is a magic menu item associated with this item */
774 if (lpitem
->text
&& IS_MAGIC_ITEM(lpitem
->text
))
779 switch(LOWORD(lpitem
->text
))
781 case (INT_PTR
)HBMMENU_SYSTEM
:
782 if (lpitem
->dwItemData
)
784 bmp
= (HBITMAP
)lpitem
->dwItemData
;
785 if (!GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
790 if (!GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
791 /* only use right half of the bitmap */
792 bmp_xoffset
= bm
.bmWidth
/ 2;
793 bm
.bmWidth
-= bmp_xoffset
;
796 case (INT_PTR
)HBMMENU_MBAR_RESTORE
:
797 flags
= DFCS_CAPTIONRESTORE
;
799 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE
:
800 flags
= DFCS_CAPTIONMIN
;
802 case (INT_PTR
)HBMMENU_MBAR_MINIMIZE_D
:
803 flags
= DFCS_CAPTIONMIN
| DFCS_INACTIVE
;
805 case (INT_PTR
)HBMMENU_MBAR_CLOSE
:
806 flags
= DFCS_CAPTIONCLOSE
;
808 case (INT_PTR
)HBMMENU_MBAR_CLOSE_D
:
809 flags
= DFCS_CAPTIONCLOSE
| DFCS_INACTIVE
;
811 case (INT_PTR
)HBMMENU_CALLBACK
:
812 case (INT_PTR
)HBMMENU_POPUP_CLOSE
:
813 case (INT_PTR
)HBMMENU_POPUP_RESTORE
:
814 case (INT_PTR
)HBMMENU_POPUP_MAXIMIZE
:
815 case (INT_PTR
)HBMMENU_POPUP_MINIMIZE
:
817 FIXME("Magic 0x%08x not implemented\n", LOWORD(lpitem
->text
));
821 InflateRect( &r
, -1, -1 );
822 if (lpitem
->fState
& MF_HILITE
) flags
|= DFCS_PUSHED
;
823 DrawFrameControl( hdc
, &r
, DFC_CAPTION
, flags
);
827 if (!bmp
|| !GetObjectA( bmp
, sizeof(bm
), &bm
)) return;
830 hdcMem
= CreateCompatibleDC( hdc
);
831 SelectObject( hdcMem
, bmp
);
833 /* handle fontsize > bitmap_height */
834 top
= (h
>bm
.bmHeight
) ? rect
->top
+(h
-bm
.bmHeight
)/2 : rect
->top
;
836 if (TWEAK_WineLook
== WIN95_LOOK
) {
837 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
)) ? NOTSRCCOPY
: SRCCOPY
;
838 if ((lpitem
->fState
& MF_HILITE
) && IS_BITMAP_ITEM(lpitem
->fType
))
839 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
843 rop
=((lpitem
->fState
& MF_HILITE
) && !IS_MAGIC_ITEM(lpitem
->text
) && (!menuBar
)) ? MERGEPAINT
: SRCCOPY
;
845 BitBlt( hdc
, left
, top
, w
, h
, hdcMem
, bmp_xoffset
, 0, rop
);
850 /***********************************************************************
853 * Calculate the size of the menu item and store it in lpitem->rect.
855 static void MENU_CalcItemSize( HDC hdc
, MENUITEM
*lpitem
, HWND hwndOwner
,
856 INT orgX
, INT orgY
, BOOL menuBar
)
859 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
861 TRACE("dc=%p owner=%p (%d,%d)\n", hdc
, hwndOwner
, orgX
, orgY
);
862 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem
,
863 (menuBar
? " (MenuBar)" : ""));
865 SetRect( &lpitem
->rect
, orgX
, orgY
, orgX
, orgY
);
867 if (lpitem
->fType
& MF_OWNERDRAW
)
870 ** Experimentation under Windows reveals that an owner-drawn
871 ** menu is expected to return the size of the content part of
872 ** the menu item, not including the checkmark nor the submenu
873 ** arrow. Windows adds those values itself and returns the
874 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
876 MEASUREITEMSTRUCT mis
;
877 mis
.CtlType
= ODT_MENU
;
879 mis
.itemID
= lpitem
->wID
;
880 mis
.itemData
= (DWORD
)lpitem
->dwItemData
;
883 SendMessageA( hwndOwner
, WM_MEASUREITEM
, 0, (LPARAM
)&mis
);
884 lpitem
->rect
.right
+= mis
.itemWidth
;
888 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
891 /* under at least win95 you seem to be given a standard
892 height for the menu and the height value is ignored */
894 if (TWEAK_WineLook
== WIN31_LOOK
)
895 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
);
897 lpitem
->rect
.bottom
+= GetSystemMetrics(SM_CYMENU
)-1;
900 lpitem
->rect
.bottom
+= mis
.itemHeight
;
902 TRACE("id=%04x size=%dx%d\n",
903 lpitem
->wID
, mis
.itemWidth
, mis
.itemHeight
);
904 /* Fall through to get check/arrow width calculation. */
907 if (lpitem
->fType
& MF_SEPARATOR
)
909 lpitem
->rect
.bottom
+= SEPARATOR_HEIGHT
;
915 lpitem
->rect
.right
+= 2 * check_bitmap_width
;
916 if (lpitem
->fType
& MF_POPUP
)
917 lpitem
->rect
.right
+= arrow_bitmap_width
;
920 if (lpitem
->fType
& MF_OWNERDRAW
)
923 if (IS_BITMAP_ITEM(lpitem
->fType
))
927 MENU_GetBitmapItemSize( (int)lpitem
->text
, lpitem
->dwItemData
, &size
);
928 lpitem
->rect
.right
+= size
.cx
;
929 lpitem
->rect
.bottom
+= size
.cy
;
930 if (TWEAK_WineLook
== WIN98_LOOK
)
932 /* Leave space for the sunken border */
933 lpitem
->rect
.right
+= 2;
934 lpitem
->rect
.bottom
+= 2;
939 /* it must be a text item - unless it's the system menu */
940 if (!(lpitem
->fType
& MF_SYSMENU
) && IS_STRING_ITEM( lpitem
->fType
))
943 GetTextExtentPoint32W(hdc
, lpitem
->text
, strlenW(lpitem
->text
), &size
);
945 lpitem
->rect
.right
+= size
.cx
;
946 if (TWEAK_WineLook
== WIN31_LOOK
)
947 lpitem
->rect
.bottom
+= max( size
.cy
, GetSystemMetrics(SM_CYMENU
) );
949 lpitem
->rect
.bottom
+= max(size
.cy
, GetSystemMetrics(SM_CYMENU
)-1);
954 lpitem
->rect
.right
+= MENU_BAR_ITEMS_SPACE
;
956 else if ((p
= strchrW( lpitem
->text
, '\t' )) != NULL
)
958 /* Item contains a tab (only meaningful in popup menus) */
959 GetTextExtentPoint32W(hdc
, lpitem
->text
, (int)(p
- lpitem
->text
) , &size
);
960 lpitem
->xTab
= check_bitmap_width
+ MENU_TAB_SPACE
+ size
.cx
;
961 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
965 if (strchrW( lpitem
->text
, '\b' ))
966 lpitem
->rect
.right
+= MENU_TAB_SPACE
;
967 lpitem
->xTab
= lpitem
->rect
.right
- check_bitmap_width
968 - arrow_bitmap_width
;
971 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem
->rect
.left
, lpitem
->rect
.top
, lpitem
->rect
.right
, lpitem
->rect
.bottom
);
975 /***********************************************************************
976 * MENU_PopupMenuCalcSize
978 * Calculate the size of a popup menu.
980 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop
, HWND hwndOwner
)
985 int orgX
, orgY
, maxX
, maxTab
, maxTabWidth
;
987 lppop
->Width
= lppop
->Height
= 0;
988 if (lppop
->nItems
== 0) return;
991 SelectObject( hdc
, hMenuFont
);
994 maxX
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CXBORDER
) : 2+1 ;
996 while (start
< lppop
->nItems
)
998 lpitem
= &lppop
->items
[start
];
1000 orgY
= (TWEAK_WineLook
== WIN31_LOOK
) ? GetSystemMetrics(SM_CYBORDER
) : 2;
1002 maxTab
= maxTabWidth
= 0;
1004 /* Parse items until column break or end of menu */
1005 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1008 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1010 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, FALSE
);
1012 if (lpitem
->fType
& MF_MENUBARBREAK
) orgX
++;
1013 maxX
= max( maxX
, lpitem
->rect
.right
);
1014 orgY
= lpitem
->rect
.bottom
;
1015 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1017 maxTab
= max( maxTab
, lpitem
->xTab
);
1018 maxTabWidth
= max(maxTabWidth
,lpitem
->rect
.right
-lpitem
->xTab
);
1022 /* Finish the column (set all items to the largest width found) */
1023 maxX
= max( maxX
, maxTab
+ maxTabWidth
);
1024 for (lpitem
= &lppop
->items
[start
]; start
< i
; start
++, lpitem
++)
1026 lpitem
->rect
.right
= maxX
;
1027 if (IS_STRING_ITEM(lpitem
->fType
) && lpitem
->xTab
)
1028 lpitem
->xTab
= maxTab
;
1031 lppop
->Height
= max( lppop
->Height
, orgY
);
1034 lppop
->Width
= maxX
;
1036 /* space for 3d border */
1037 if(TWEAK_WineLook
> WIN31_LOOK
)
1043 ReleaseDC( 0, hdc
);
1047 /***********************************************************************
1048 * MENU_MenuBarCalcSize
1050 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1051 * height is off by 1 pixel which causes lengthy window relocations when
1052 * active document window is maximized/restored.
1054 * Calculate the size of the menu bar.
1056 static void MENU_MenuBarCalcSize( HDC hdc
, LPRECT lprect
,
1057 LPPOPUPMENU lppop
, HWND hwndOwner
)
1060 int start
, i
, orgX
, orgY
, maxY
, helpPos
;
1062 if ((lprect
== NULL
) || (lppop
== NULL
)) return;
1063 if (lppop
->nItems
== 0) return;
1064 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1065 lprect
->left
, lprect
->top
, lprect
->right
, lprect
->bottom
);
1066 lppop
->Width
= lprect
->right
- lprect
->left
;
1068 maxY
= lprect
->top
+1;
1071 while (start
< lppop
->nItems
)
1073 lpitem
= &lppop
->items
[start
];
1074 orgX
= lprect
->left
;
1077 /* Parse items until line break or end of menu */
1078 for (i
= start
; i
< lppop
->nItems
; i
++, lpitem
++)
1080 if ((helpPos
== -1) && (lpitem
->fType
& MF_RIGHTJUSTIFY
)) helpPos
= i
;
1082 (lpitem
->fType
& (MF_MENUBREAK
| MF_MENUBARBREAK
))) break;
1084 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n",
1086 debug_print_menuitem (" item: ", lpitem
, "");
1087 MENU_CalcItemSize( hdc
, lpitem
, hwndOwner
, orgX
, orgY
, TRUE
);
1089 if (lpitem
->rect
.right
> lprect
->right
)
1091 if (i
!= start
) break;
1092 else lpitem
->rect
.right
= lprect
->right
;
1094 maxY
= max( maxY
, lpitem
->rect
.bottom
);
1095 orgX
= lpitem
->rect
.right
;
1098 /* Finish the line (set all items to the largest height found) */
1099 while (start
< i
) lppop
->items
[start
++].rect
.bottom
= maxY
;
1102 lprect
->bottom
= maxY
;
1103 lppop
->Height
= lprect
->bottom
- lprect
->top
;
1105 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1106 /* the last item (if several lines, only move the last line) */
1107 lpitem
= &lppop
->items
[lppop
->nItems
-1];
1108 orgY
= lpitem
->rect
.top
;
1109 orgX
= lprect
->right
;
1110 for (i
= lppop
->nItems
- 1; i
>= helpPos
; i
--, lpitem
--) {
1111 if ( (helpPos
==-1) || (helpPos
>i
) )
1113 if (lpitem
->rect
.top
!= orgY
) break; /* Other line */
1114 if (lpitem
->rect
.right
>= orgX
) break; /* Too far right already */
1115 lpitem
->rect
.left
+= orgX
- lpitem
->rect
.right
;
1116 lpitem
->rect
.right
= orgX
;
1117 orgX
= lpitem
->rect
.left
;
1121 /***********************************************************************
1124 * Draw a single menu item.
1126 static void MENU_DrawMenuItem( HWND hwnd
, HMENU hmenu
, HWND hwndOwner
, HDC hdc
, MENUITEM
*lpitem
,
1127 UINT height
, BOOL menuBar
, UINT odaction
)
1131 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem
, "");
1133 if (lpitem
->fType
& MF_SYSMENU
)
1135 if( !IsIconic(hwnd
) ) {
1136 if (TWEAK_WineLook
> WIN31_LOOK
)
1137 NC_DrawSysButton95( hwnd
, hdc
,
1139 (MF_HILITE
| MF_MOUSESELECT
) );
1141 NC_DrawSysButton( hwnd
, hdc
,
1143 (MF_HILITE
| MF_MOUSESELECT
) );
1149 if (lpitem
->fType
& MF_OWNERDRAW
)
1152 ** Experimentation under Windows reveals that an owner-drawn
1153 ** menu is given the rectangle which includes the space it requested
1154 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1155 ** and a popup-menu arrow. This is the value of lpitem->rect.
1156 ** Windows will leave all drawing to the application except for
1157 ** the popup-menu arrow. Windows always draws that itself, after
1158 ** the menu owner has finished drawing.
1162 dis
.CtlType
= ODT_MENU
;
1164 dis
.itemID
= lpitem
->wID
;
1165 dis
.itemData
= (DWORD
)lpitem
->dwItemData
;
1167 if (lpitem
->fState
& MF_CHECKED
) dis
.itemState
|= ODS_CHECKED
;
1168 if (lpitem
->fState
& MF_GRAYED
) dis
.itemState
|= ODS_GRAYED
|ODS_DISABLED
;
1169 if (lpitem
->fState
& MF_HILITE
) dis
.itemState
|= ODS_SELECTED
;
1170 dis
.itemAction
= odaction
; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1171 dis
.hwndItem
= (HWND
)hmenu
;
1173 dis
.rcItem
= lpitem
->rect
;
1174 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1175 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner
,
1176 dis
.itemID
, dis
.itemState
, dis
.itemAction
, dis
.hwndItem
,
1177 dis
.hDC
, dis
.rcItem
.left
, dis
.rcItem
.top
, dis
.rcItem
.right
,
1179 SendMessageA( hwndOwner
, WM_DRAWITEM
, 0, (LPARAM
)&dis
);
1180 /* Fall through to draw popup-menu arrow */
1183 TRACE("rect={%ld,%ld,%ld,%ld}\n", lpitem
->rect
.left
, lpitem
->rect
.top
,
1184 lpitem
->rect
.right
,lpitem
->rect
.bottom
);
1186 if (menuBar
&& (lpitem
->fType
& MF_SEPARATOR
)) return;
1188 rect
= lpitem
->rect
;
1190 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1192 if (lpitem
->fState
& MF_HILITE
)
1194 if(TWEAK_WineLook
== WIN98_LOOK
)
1197 DrawEdge(hdc
, &rect
, BDR_SUNKENOUTER
, BF_RECT
);
1199 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1201 else /* Not Win98 Look */
1203 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1204 FillRect(hdc
, &rect
, GetSysColorBrush(COLOR_HIGHLIGHT
));
1208 FillRect( hdc
, &rect
, GetSysColorBrush(COLOR_MENU
) );
1211 SetBkMode( hdc
, TRANSPARENT
);
1213 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1215 /* vertical separator */
1216 if (!menuBar
&& (lpitem
->fType
& MF_MENUBARBREAK
))
1218 if (TWEAK_WineLook
> WIN31_LOOK
)
1222 rc
.bottom
= height
- 3;
1223 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_LEFT
);
1227 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1228 MoveToEx( hdc
, rect
.left
, 0, NULL
);
1229 LineTo( hdc
, rect
.left
, height
);
1233 /* horizontal separator */
1234 if (lpitem
->fType
& MF_SEPARATOR
)
1236 if (TWEAK_WineLook
> WIN31_LOOK
)
1241 rc
.top
+= SEPARATOR_HEIGHT
/ 2;
1242 DrawEdge (hdc
, &rc
, EDGE_ETCHED
, BF_TOP
);
1246 SelectObject( hdc
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
1247 MoveToEx( hdc
, rect
.left
, rect
.top
+ SEPARATOR_HEIGHT
/2, NULL
);
1248 LineTo( hdc
, rect
.right
, rect
.top
+ SEPARATOR_HEIGHT
/2 );
1256 if (lpitem
->fState
& MF_HILITE
)
1258 if(TWEAK_WineLook
== WIN98_LOOK
)
1261 SetTextColor(hdc
, GetSysColor(COLOR_MENUTEXT
));
1262 SetBkColor(hdc
, GetSysColor(COLOR_MENU
));
1264 if(lpitem
->fState
& MF_GRAYED
)
1265 SetTextColor(hdc
, GetSysColor(COLOR_GRAYTEXT
));
1267 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1268 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1271 else /* Not Win98 Look */
1273 SetTextColor(hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
1274 if(!IS_BITMAP_ITEM(lpitem
->fType
))
1275 SetBkColor(hdc
, GetSysColor(COLOR_HIGHLIGHT
));
1280 if (lpitem
->fState
& MF_GRAYED
)
1281 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
1283 SetTextColor( hdc
, GetSysColor( COLOR_MENUTEXT
) );
1284 SetBkColor( hdc
, GetSysColor( COLOR_MENU
) );
1287 /* helper lines for debugging */
1288 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1289 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1290 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1291 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1296 INT y
= rect
.top
+ rect
.bottom
;
1297 UINT check_bitmap_width
= GetSystemMetrics( SM_CXMENUCHECK
);
1298 UINT check_bitmap_height
= GetSystemMetrics( SM_CYMENUCHECK
);
1300 if (!(lpitem
->fType
& MF_OWNERDRAW
))
1302 /* Draw the check mark
1305 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1307 HBITMAP bm
= (lpitem
->fState
& MF_CHECKED
) ? lpitem
->hCheckBit
: lpitem
->hUnCheckBit
;
1308 if (bm
) /* we have a custom bitmap */
1310 HDC hdcMem
= CreateCompatibleDC( hdc
);
1311 SelectObject( hdcMem
, bm
);
1312 BitBlt( hdc
, rect
.left
, (y
- check_bitmap_height
) / 2,
1313 check_bitmap_width
, check_bitmap_height
,
1314 hdcMem
, 0, 0, SRCCOPY
);
1317 else if (lpitem
->fState
& MF_CHECKED
) /* standard bitmaps */
1320 HBITMAP bm
= CreateBitmap( check_bitmap_width
, check_bitmap_height
, 1, 1, NULL
);
1321 HDC hdcMem
= CreateCompatibleDC( hdc
);
1322 SelectObject( hdcMem
, bm
);
1323 SetRect( &r
, 0, 0, check_bitmap_width
, check_bitmap_height
);
1324 DrawFrameControl( hdcMem
, &r
, DFC_MENU
,
1325 (lpitem
->fType
& MFT_RADIOCHECK
) ?
1326 DFCS_MENUBULLET
: DFCS_MENUCHECK
);
1327 BitBlt( hdc
, rect
.left
, (y
- r
.bottom
) / 2, r
.right
, r
.bottom
,
1328 hdcMem
, 0, 0, SRCCOPY
);
1334 /* Draw the popup-menu arrow */
1335 if (lpitem
->fType
& MF_POPUP
)
1337 HDC hdcMem
= CreateCompatibleDC( hdc
);
1338 HBITMAP hOrigBitmap
;
1340 hOrigBitmap
= SelectObject( hdcMem
, hStdMnArrow
);
1341 BitBlt( hdc
, rect
.right
- arrow_bitmap_width
- 1,
1342 (y
- arrow_bitmap_height
) / 2,
1343 arrow_bitmap_width
, arrow_bitmap_height
,
1344 hdcMem
, 0, 0, SRCCOPY
);
1345 SelectObject( hdcMem
, hOrigBitmap
);
1349 rect
.left
+= check_bitmap_width
;
1350 rect
.right
-= arrow_bitmap_width
;
1353 /* Done for owner-drawn */
1354 if (lpitem
->fType
& MF_OWNERDRAW
)
1357 /* Draw the item text or bitmap */
1358 if (IS_BITMAP_ITEM(lpitem
->fType
))
1360 MENU_DrawBitmapItem( hdc
, lpitem
, &rect
, menuBar
);
1364 /* No bitmap - process text if present */
1365 else if (IS_STRING_ITEM(lpitem
->fType
))
1370 UINT uFormat
= (menuBar
) ?
1371 DT_CENTER
| DT_VCENTER
| DT_SINGLELINE
:
1372 DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1374 if ( lpitem
->fState
& MFS_DEFAULT
)
1376 hfontOld
= SelectObject( hdc
, hMenuFontBold
);
1381 rect
.left
+= MENU_BAR_ITEMS_SPACE
/ 2;
1382 rect
.right
-= MENU_BAR_ITEMS_SPACE
/ 2;
1385 for (i
= 0; lpitem
->text
[i
]; i
++)
1386 if ((lpitem
->text
[i
] == '\t') || (lpitem
->text
[i
] == '\b'))
1389 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1391 if (!(lpitem
->fState
& MF_HILITE
) )
1393 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1394 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1395 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1396 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1398 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1401 DrawTextW( hdc
, lpitem
->text
, i
, &rect
, uFormat
);
1403 /* paint the shortcut text */
1404 if (!menuBar
&& lpitem
->text
[i
]) /* There's a tab or flush-right char */
1406 if (lpitem
->text
[i
] == '\t')
1408 rect
.left
= lpitem
->xTab
;
1409 uFormat
= DT_LEFT
| DT_VCENTER
| DT_SINGLELINE
;
1413 uFormat
= DT_RIGHT
| DT_VCENTER
| DT_SINGLELINE
;
1416 if( (TWEAK_WineLook
!= WIN31_LOOK
) && (lpitem
->fState
& MF_GRAYED
))
1418 if (!(lpitem
->fState
& MF_HILITE
) )
1420 ++rect
.left
; ++rect
.top
; ++rect
.right
; ++rect
.bottom
;
1421 SetTextColor(hdc
, RGB(0xff, 0xff, 0xff));
1422 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1423 --rect
.left
; --rect
.top
; --rect
.right
; --rect
.bottom
;
1425 SetTextColor(hdc
, RGB(0x80, 0x80, 0x80));
1427 DrawTextW( hdc
, lpitem
->text
+ i
+ 1, -1, &rect
, uFormat
);
1431 SelectObject (hdc
, hfontOld
);
1436 /***********************************************************************
1437 * MENU_DrawPopupMenu
1439 * Paint a popup menu.
1441 static void MENU_DrawPopupMenu( HWND hwnd
, HDC hdc
, HMENU hmenu
)
1443 HBRUSH hPrevBrush
= 0;
1446 TRACE("wnd=%p dc=%p menu=%p\n", hwnd
, hdc
, hmenu
);
1448 GetClientRect( hwnd
, &rect
);
1450 if(TWEAK_WineLook
== WIN31_LOOK
)
1452 rect
.bottom
-= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1453 rect
.right
-= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
);
1456 if((hPrevBrush
= SelectObject( hdc
, GetSysColorBrush(COLOR_MENU
) ))
1457 && (SelectObject( hdc
, hMenuFont
)))
1461 Rectangle( hdc
, rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
1463 hPrevPen
= SelectObject( hdc
, GetStockObject( NULL_PEN
) );
1469 /* draw 3-d shade */
1470 if(TWEAK_WineLook
== WIN31_LOOK
) {
1471 SelectObject( hdc
, hShadeBrush
);
1472 SetBkMode( hdc
, TRANSPARENT
);
1473 ropPrev
= SetROP2( hdc
, R2_MASKPEN
);
1475 i
= rect
.right
; /* why SetBrushOrg() doesn't? */
1476 PatBlt( hdc
, i
& 0xfffffffe,
1477 rect
.top
+ POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
),
1478 i
%2 + POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1479 rect
.bottom
- rect
.top
, 0x00a000c9 );
1481 PatBlt( hdc
, rect
.left
+ POPUP_XSHADE
*GetSystemMetrics(SM_CXBORDER
),
1482 i
& 0xfffffffe,rect
.right
- rect
.left
,
1483 i
%2 + POPUP_YSHADE
*GetSystemMetrics(SM_CYBORDER
), 0x00a000c9 );
1484 SelectObject( hdc
, hPrevPen
);
1485 SelectObject( hdc
, hPrevBrush
);
1486 SetROP2( hdc
, ropPrev
);
1489 DrawEdge (hdc
, &rect
, EDGE_RAISED
, BF_RECT
);
1491 /* draw menu items */
1493 menu
= MENU_GetMenu( hmenu
);
1494 if (menu
&& menu
->nItems
)
1499 for (u
= menu
->nItems
, item
= menu
->items
; u
> 0; u
--, item
++)
1500 MENU_DrawMenuItem( hwnd
, hmenu
, menu
->hwndOwner
, hdc
, item
,
1501 menu
->Height
, FALSE
, ODA_DRAWENTIRE
);
1506 SelectObject( hdc
, hPrevBrush
);
1511 /***********************************************************************
1514 * Paint a menu bar. Returns the height of the menu bar.
1515 * called from [windows/nonclient.c]
1517 UINT
MENU_DrawMenuBar( HDC hDC
, LPRECT lprect
, HWND hwnd
,
1522 HMENU hMenu
= GetMenu(hwnd
);
1524 lppop
= MENU_GetMenu( hMenu
);
1525 if (lppop
== NULL
|| lprect
== NULL
)
1527 return GetSystemMetrics(SM_CYMENU
);
1532 hfontOld
= SelectObject( hDC
, hMenuFont
);
1534 if (lppop
->Height
== 0)
1535 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
1537 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
1539 if (hfontOld
) SelectObject( hDC
, hfontOld
);
1540 return lppop
->Height
;
1543 return DrawMenuBarTemp(hwnd
, hDC
, lprect
, hMenu
, NULL
);
1547 /***********************************************************************
1550 * Display a popup menu.
1552 static BOOL
MENU_ShowPopup( HWND hwndOwner
, HMENU hmenu
, UINT id
,
1553 INT x
, INT y
, INT xanchor
, INT yanchor
)
1558 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1559 hwndOwner
, hmenu
, id
, x
, y
, xanchor
, yanchor
);
1561 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
1562 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1564 menu
->items
[menu
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1565 menu
->FocusedItem
= NO_SELECTED_ITEM
;
1568 /* store the owner for DrawItem */
1569 menu
->hwndOwner
= hwndOwner
;
1572 MENU_PopupMenuCalcSize( menu
, hwndOwner
);
1574 /* adjust popup menu pos so that it fits within the desktop */
1576 width
= menu
->Width
+ GetSystemMetrics(SM_CXBORDER
);
1577 height
= menu
->Height
+ GetSystemMetrics(SM_CYBORDER
);
1579 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1582 x
-= width
- xanchor
;
1583 if( x
+ width
> GetSystemMetrics(SM_CXSCREEN
))
1584 x
= GetSystemMetrics(SM_CXSCREEN
) - width
;
1588 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1591 y
-= height
+ yanchor
;
1592 if( y
+ height
> GetSystemMetrics(SM_CYSCREEN
))
1593 y
= GetSystemMetrics(SM_CYSCREEN
) - height
;
1597 if( TWEAK_WineLook
== WIN31_LOOK
)
1599 width
+= POPUP_XSHADE
* GetSystemMetrics(SM_CXBORDER
); /* add space for shading */
1600 height
+= POPUP_YSHADE
* GetSystemMetrics(SM_CYBORDER
);
1603 /* NOTE: In Windows, top menu popup is not owned. */
1604 menu
->hWnd
= CreateWindowA( POPUPMENU_CLASS_ATOM
, NULL
,
1605 WS_POPUP
, x
, y
, width
, height
,
1606 hwndOwner
, 0, (HINSTANCE
)GetWindowLongA(hwndOwner
,GWL_HINSTANCE
),
1608 if( !menu
->hWnd
) return FALSE
;
1609 if (!top_popup
) top_popup
= menu
->hWnd
;
1611 /* Display the window */
1613 SetWindowPos( menu
->hWnd
, HWND_TOP
, 0, 0, 0, 0,
1614 SWP_SHOWWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
1615 UpdateWindow( menu
->hWnd
);
1620 /***********************************************************************
1623 static void MENU_SelectItem( HWND hwndOwner
, HMENU hmenu
, UINT wIndex
,
1624 BOOL sendMenuSelect
, HMENU topmenu
)
1629 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner
, hmenu
, wIndex
, sendMenuSelect
);
1631 lppop
= MENU_GetMenu( hmenu
);
1632 if ((!lppop
) || (!lppop
->nItems
) || (!lppop
->hWnd
)) return;
1634 if (lppop
->FocusedItem
== wIndex
) return;
1635 if (lppop
->wFlags
& MF_POPUP
) hdc
= GetDC( lppop
->hWnd
);
1636 else hdc
= GetDCEx( lppop
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
1637 if (!top_popup
) top_popup
= lppop
->hWnd
;
1639 SelectObject( hdc
, hMenuFont
);
1641 /* Clear previous highlighted item */
1642 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1644 lppop
->items
[lppop
->FocusedItem
].fState
&= ~(MF_HILITE
|MF_MOUSESELECT
);
1645 MENU_DrawMenuItem(lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,&lppop
->items
[lppop
->FocusedItem
],
1646 lppop
->Height
, !(lppop
->wFlags
& MF_POPUP
),
1650 /* Highlight new item (if any) */
1651 lppop
->FocusedItem
= wIndex
;
1652 if (lppop
->FocusedItem
!= NO_SELECTED_ITEM
)
1654 if(!(lppop
->items
[wIndex
].fType
& MF_SEPARATOR
)) {
1655 lppop
->items
[wIndex
].fState
|= MF_HILITE
;
1656 MENU_DrawMenuItem( lppop
->hWnd
, hmenu
, hwndOwner
, hdc
,
1657 &lppop
->items
[wIndex
], lppop
->Height
,
1658 !(lppop
->wFlags
& MF_POPUP
), ODA_SELECT
);
1662 MENUITEM
*ip
= &lppop
->items
[lppop
->FocusedItem
];
1663 SendMessageA( hwndOwner
, WM_MENUSELECT
,
1664 MAKELONG(ip
->fType
& MF_POPUP
? wIndex
: ip
->wID
,
1665 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1666 (lppop
->wFlags
& MF_SYSMENU
)), (LPARAM
)hmenu
);
1669 else if (sendMenuSelect
) {
1672 if((pos
=MENU_FindSubMenu(&topmenu
, hmenu
))!=NO_SELECTED_ITEM
){
1673 POPUPMENU
*ptm
= MENU_GetMenu( topmenu
);
1674 MENUITEM
*ip
= &ptm
->items
[pos
];
1675 SendMessageA( hwndOwner
, WM_MENUSELECT
, MAKELONG(pos
,
1676 ip
->fType
| ip
->fState
| MF_MOUSESELECT
|
1677 (ptm
->wFlags
& MF_SYSMENU
)), (LPARAM
)topmenu
);
1681 ReleaseDC( lppop
->hWnd
, hdc
);
1685 /***********************************************************************
1686 * MENU_MoveSelection
1688 * Moves currently selected item according to the offset parameter.
1689 * If there is no selection then it should select the last item if
1690 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
1692 static void MENU_MoveSelection( HWND hwndOwner
, HMENU hmenu
, INT offset
)
1697 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner
, hmenu
, offset
);
1699 menu
= MENU_GetMenu( hmenu
);
1700 if ((!menu
) || (!menu
->items
)) return;
1702 if ( menu
->FocusedItem
!= NO_SELECTED_ITEM
)
1704 if( menu
->nItems
== 1 ) return; else
1705 for (i
= menu
->FocusedItem
+ offset
; i
>= 0 && i
< menu
->nItems
1707 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1709 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1714 for ( i
= (offset
> 0) ? 0 : menu
->nItems
- 1;
1715 i
>= 0 && i
< menu
->nItems
; i
+= offset
)
1716 if (!(menu
->items
[i
].fType
& MF_SEPARATOR
))
1718 MENU_SelectItem( hwndOwner
, hmenu
, i
, TRUE
, 0 );
1724 /**********************************************************************
1727 * Set an item's flags, id and text ptr. Called by InsertMenu() and
1730 static BOOL
MENU_SetItemData( MENUITEM
*item
, UINT flags
, UINT_PTR id
,
1733 LPWSTR prevText
= IS_STRING_ITEM(item
->fType
) ? item
->text
: NULL
;
1735 debug_print_menuitem("MENU_SetItemData from: ", item
, "");
1736 TRACE("flags=%x str=%p\n", flags
, str
);
1738 if (IS_STRING_ITEM(flags
))
1742 flags
|= MF_SEPARATOR
;
1748 /* Item beginning with a backspace is a help item */
1754 if (!(text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(str
)+1) * sizeof(WCHAR
) )))
1756 strcpyW( text
, str
);
1760 else if (IS_BITMAP_ITEM(flags
))
1761 item
->text
= (LPWSTR
)HBITMAP_32(LOWORD(str
));
1762 else item
->text
= NULL
;
1764 if (flags
& MF_OWNERDRAW
)
1765 item
->dwItemData
= (DWORD
)str
;
1767 item
->dwItemData
= 0;
1769 if ((item
->fType
& MF_POPUP
) && (flags
& MF_POPUP
) && (item
->hSubMenu
!= (HMENU
)id
) )
1770 DestroyMenu( item
->hSubMenu
); /* ModifyMenu() spec */
1772 if (flags
& MF_POPUP
)
1774 POPUPMENU
*menu
= MENU_GetMenu((HMENU
)id
);
1775 if (menu
) menu
->wFlags
|= MF_POPUP
;
1787 if (flags
& MF_POPUP
) item
->hSubMenu
= (HMENU
)id
;
1789 if ((item
->fType
& MF_POPUP
) && !(flags
& MF_POPUP
) )
1790 flags
|= MF_POPUP
; /* keep popup */
1792 item
->fType
= flags
& TYPE_MASK
;
1793 item
->fState
= (flags
& STATE_MASK
) &
1794 ~(MF_HILITE
| MF_MOUSESELECT
| MF_BYPOSITION
);
1797 /* Don't call SetRectEmpty here! */
1800 if (prevText
) HeapFree( GetProcessHeap(), 0, prevText
);
1802 debug_print_menuitem("MENU_SetItemData to : ", item
, "");
1807 /**********************************************************************
1810 * Insert (allocate) a new item into a menu.
1812 static MENUITEM
*MENU_InsertItem( HMENU hMenu
, UINT pos
, UINT flags
)
1817 if (!(menu
= MENU_GetMenu(hMenu
)))
1820 /* Find where to insert new item */
1822 if (flags
& MF_BYPOSITION
) {
1823 if (pos
> menu
->nItems
)
1826 if (!MENU_FindItem( &hMenu
, &pos
, flags
))
1829 if (!(menu
= MENU_GetMenu( hMenu
)))
1834 /* Create new items array */
1836 newItems
= HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM
) * (menu
->nItems
+1) );
1839 WARN("allocation failed\n" );
1842 if (menu
->nItems
> 0)
1844 /* Copy the old array into the new one */
1845 if (pos
> 0) memcpy( newItems
, menu
->items
, pos
* sizeof(MENUITEM
) );
1846 if (pos
< menu
->nItems
) memcpy( &newItems
[pos
+1], &menu
->items
[pos
],
1847 (menu
->nItems
-pos
)*sizeof(MENUITEM
) );
1848 HeapFree( GetProcessHeap(), 0, menu
->items
);
1850 menu
->items
= newItems
;
1852 memset( &newItems
[pos
], 0, sizeof(*newItems
) );
1853 menu
->Height
= 0; /* force size recalculate */
1854 return &newItems
[pos
];
1858 /**********************************************************************
1859 * MENU_ParseResource
1861 * Parse a standard menu resource and add items to the menu.
1862 * Return a pointer to the end of the resource.
1864 * NOTE: flags is equivalent to the mtOption field
1866 static LPCSTR
MENU_ParseResource( LPCSTR res
, HMENU hMenu
, BOOL unicode
)
1873 flags
= GET_WORD(res
);
1874 res
+= sizeof(WORD
);
1875 if (!(flags
& MF_POPUP
))
1878 res
+= sizeof(WORD
);
1881 if (!unicode
) res
+= strlen(str
) + 1;
1882 else res
+= (strlenW((LPCWSTR
)str
) + 1) * sizeof(WCHAR
);
1883 if (flags
& MF_POPUP
)
1885 HMENU hSubMenu
= CreatePopupMenu();
1886 if (!hSubMenu
) return NULL
;
1887 if (!(res
= MENU_ParseResource( res
, hSubMenu
, unicode
)))
1889 if (!unicode
) AppendMenuA( hMenu
, flags
, (UINT
)hSubMenu
, str
);
1890 else AppendMenuW( hMenu
, flags
, (UINT
)hSubMenu
, (LPCWSTR
)str
);
1892 else /* Not a popup */
1894 if (!unicode
) AppendMenuA( hMenu
, flags
, id
, *str
? str
: NULL
);
1895 else AppendMenuW( hMenu
, flags
, id
,
1896 *(LPCWSTR
)str
? (LPCWSTR
)str
: NULL
);
1898 } while (!(flags
& MF_END
));
1903 /**********************************************************************
1904 * MENUEX_ParseResource
1906 * Parse an extended menu resource and add items to the menu.
1907 * Return a pointer to the end of the resource.
1909 static LPCSTR
MENUEX_ParseResource( LPCSTR res
, HMENU hMenu
)
1915 mii
.cbSize
= sizeof(mii
);
1916 mii
.fMask
= MIIM_STATE
| MIIM_ID
| MIIM_TYPE
;
1917 mii
.fType
= GET_DWORD(res
);
1918 res
+= sizeof(DWORD
);
1919 mii
.fState
= GET_DWORD(res
);
1920 res
+= sizeof(DWORD
);
1921 mii
.wID
= GET_DWORD(res
);
1922 res
+= sizeof(DWORD
);
1923 resinfo
= GET_WORD(res
); /* FIXME: for 16-bit apps this is a byte. */
1924 res
+= sizeof(WORD
);
1925 /* Align the text on a word boundary. */
1926 res
+= (~((int)res
- 1)) & 1;
1927 mii
.dwTypeData
= (LPWSTR
) res
;
1928 res
+= (1 + strlenW(mii
.dwTypeData
)) * sizeof(WCHAR
);
1929 /* Align the following fields on a dword boundary. */
1930 res
+= (~((int)res
- 1)) & 3;
1932 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
1933 mii
.fType
, mii
.fState
, mii
.wID
, resinfo
, debugstr_w(mii
.dwTypeData
));
1935 if (resinfo
& 1) { /* Pop-up? */
1936 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1937 res
+= sizeof(DWORD
);
1938 mii
.hSubMenu
= CreatePopupMenu();
1941 if (!(res
= MENUEX_ParseResource(res
, mii
.hSubMenu
))) {
1942 DestroyMenu(mii
.hSubMenu
);
1945 mii
.fMask
|= MIIM_SUBMENU
;
1946 mii
.fType
|= MF_POPUP
;
1948 else if(!*mii
.dwTypeData
&& !(mii
.fType
& MF_SEPARATOR
))
1950 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
1951 mii
.wID
, mii
.fType
);
1952 mii
.fType
|= MF_SEPARATOR
;
1954 InsertMenuItemW(hMenu
, -1, MF_BYPOSITION
, &mii
);
1955 } while (!(resinfo
& MF_END
));
1960 /***********************************************************************
1963 * Return the handle of the selected sub-popup menu (if any).
1965 static HMENU
MENU_GetSubPopup( HMENU hmenu
)
1970 menu
= MENU_GetMenu( hmenu
);
1972 if ((!menu
) || (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return 0;
1974 item
= &menu
->items
[menu
->FocusedItem
];
1975 if ((item
->fType
& MF_POPUP
) && (item
->fState
& MF_MOUSESELECT
))
1976 return item
->hSubMenu
;
1981 /***********************************************************************
1982 * MENU_HideSubPopups
1984 * Hide the sub-popup menus of this menu.
1986 static void MENU_HideSubPopups( HWND hwndOwner
, HMENU hmenu
,
1987 BOOL sendMenuSelect
)
1989 POPUPMENU
*menu
= MENU_GetMenu( hmenu
);
1991 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, sendMenuSelect
);
1993 if (menu
&& top_popup
)
1999 if (menu
->FocusedItem
!= NO_SELECTED_ITEM
)
2001 item
= &menu
->items
[menu
->FocusedItem
];
2002 if (!(item
->fType
& MF_POPUP
) ||
2003 !(item
->fState
& MF_MOUSESELECT
)) return;
2004 item
->fState
&= ~MF_MOUSESELECT
;
2005 hsubmenu
= item
->hSubMenu
;
2008 submenu
= MENU_GetMenu( hsubmenu
);
2009 MENU_HideSubPopups( hwndOwner
, hsubmenu
, FALSE
);
2010 MENU_SelectItem( hwndOwner
, hsubmenu
, NO_SELECTED_ITEM
, sendMenuSelect
, 0 );
2011 DestroyWindow( submenu
->hWnd
);
2017 /***********************************************************************
2020 * Display the sub-menu of the selected item of this menu.
2021 * Return the handle of the submenu, or hmenu if no submenu to display.
2023 static HMENU
MENU_ShowSubPopup( HWND hwndOwner
, HMENU hmenu
,
2024 BOOL selectFirst
, UINT wFlags
)
2031 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner
, hmenu
, selectFirst
);
2033 if (!(menu
= MENU_GetMenu( hmenu
))) return hmenu
;
2035 if (menu
->FocusedItem
== NO_SELECTED_ITEM
) return hmenu
;
2037 item
= &menu
->items
[menu
->FocusedItem
];
2038 if (!(item
->fType
& MF_POPUP
) || (item
->fState
& (MF_GRAYED
| MF_DISABLED
)))
2041 /* message must be sent before using item,
2042 because nearly everything may be changed by the application ! */
2044 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2045 if (!(wFlags
& TPM_NONOTIFY
))
2046 SendMessageA( hwndOwner
, WM_INITMENUPOPUP
, (WPARAM
)item
->hSubMenu
,
2047 MAKELONG( menu
->FocusedItem
, IS_SYSTEM_MENU(menu
) ));
2049 item
= &menu
->items
[menu
->FocusedItem
];
2052 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2053 if (!(item
->fState
& MF_HILITE
))
2055 if (menu
->wFlags
& MF_POPUP
) hdc
= GetDC( menu
->hWnd
);
2056 else hdc
= GetDCEx( menu
->hWnd
, 0, DCX_CACHE
| DCX_WINDOW
);
2058 SelectObject( hdc
, hMenuFont
);
2060 item
->fState
|= MF_HILITE
;
2061 MENU_DrawMenuItem( menu
->hWnd
, hmenu
, hwndOwner
, hdc
, item
, menu
->Height
, !(menu
->wFlags
& MF_POPUP
), ODA_DRAWENTIRE
);
2062 ReleaseDC( menu
->hWnd
, hdc
);
2064 if (!item
->rect
.top
&& !item
->rect
.left
&& !item
->rect
.bottom
&& !item
->rect
.right
)
2067 item
->fState
|= MF_MOUSESELECT
;
2069 if (IS_SYSTEM_MENU(menu
))
2071 MENU_InitSysMenuPopup(item
->hSubMenu
,
2072 GetWindowLongA( menu
->hWnd
, GWL_STYLE
),
2073 GetClassLongA( menu
->hWnd
, GCL_STYLE
));
2075 NC_GetSysPopupPos( menu
->hWnd
, &rect
);
2076 rect
.top
= rect
.bottom
;
2077 rect
.right
= GetSystemMetrics(SM_CXSIZE
);
2078 rect
.bottom
= GetSystemMetrics(SM_CYSIZE
);
2082 GetWindowRect( menu
->hWnd
, &rect
);
2083 if (menu
->wFlags
& MF_POPUP
)
2085 rect
.left
+= item
->rect
.right
- GetSystemMetrics(SM_CXBORDER
);
2086 rect
.top
+= item
->rect
.top
;
2087 rect
.right
= item
->rect
.left
- item
->rect
.right
+ GetSystemMetrics(SM_CXBORDER
);
2088 rect
.bottom
= item
->rect
.top
- item
->rect
.bottom
;
2092 rect
.left
+= item
->rect
.left
;
2093 rect
.top
+= item
->rect
.bottom
;
2094 rect
.right
= item
->rect
.right
- item
->rect
.left
;
2095 rect
.bottom
= item
->rect
.bottom
- item
->rect
.top
;
2099 MENU_ShowPopup( hwndOwner
, item
->hSubMenu
, menu
->FocusedItem
,
2100 rect
.left
, rect
.top
, rect
.right
, rect
.bottom
);
2102 MENU_MoveSelection( hwndOwner
, item
->hSubMenu
, ITEM_NEXT
);
2103 return item
->hSubMenu
;
2108 /**********************************************************************
2111 BOOL
MENU_IsMenuActive(void)
2113 return (top_popup
!= 0);
2116 /***********************************************************************
2119 * Walks menu chain trying to find a menu pt maps to.
2121 static HMENU
MENU_PtMenu( HMENU hMenu
, POINT pt
)
2123 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2124 UINT item
= menu
->FocusedItem
;
2127 /* try subpopup first (if any) */
2128 ret
= (item
!= NO_SELECTED_ITEM
&&
2129 (menu
->items
[item
].fType
& MF_POPUP
) &&
2130 (menu
->items
[item
].fState
& MF_MOUSESELECT
))
2131 ? MENU_PtMenu(menu
->items
[item
].hSubMenu
, pt
) : 0;
2133 if (!ret
) /* check the current window (avoiding WM_HITTEST) */
2135 INT ht
= NC_HandleNCHitTest( menu
->hWnd
, pt
);
2136 if( menu
->wFlags
& MF_POPUP
)
2138 if (ht
!= HTNOWHERE
&& ht
!= HTERROR
) ret
= hMenu
;
2140 else if (ht
== HTSYSMENU
)
2141 ret
= get_win_sys_menu( menu
->hWnd
);
2142 else if (ht
== HTMENU
)
2143 ret
= GetMenu( menu
->hWnd
);
2148 /***********************************************************************
2149 * MENU_ExecFocusedItem
2151 * Execute a menu item (for instance when user pressed Enter).
2152 * Return the wID of the executed item. Otherwise, -1 indicating
2153 * that no menu item was executed;
2154 * Have to receive the flags for the TrackPopupMenu options to avoid
2155 * sending unwanted message.
2158 static INT
MENU_ExecFocusedItem( MTRACKER
* pmt
, HMENU hMenu
, UINT wFlags
)
2161 POPUPMENU
*menu
= MENU_GetMenu( hMenu
);
2163 TRACE("%p hmenu=%p\n", pmt
, hMenu
);
2165 if (!menu
|| !menu
->nItems
||
2166 (menu
->FocusedItem
== NO_SELECTED_ITEM
)) return -1;
2168 item
= &menu
->items
[menu
->FocusedItem
];
2170 TRACE("%p %08x %p\n", hMenu
, item
->wID
, item
->hSubMenu
);
2172 if (!(item
->fType
& MF_POPUP
))
2174 if (!(item
->fState
& (MF_GRAYED
| MF_DISABLED
)) && !(item
->fType
& MF_SEPARATOR
))
2176 /* If TPM_RETURNCMD is set you return the id, but
2177 do not send a message to the owner */
2178 if(!(wFlags
& TPM_RETURNCMD
))
2180 if( menu
->wFlags
& MF_SYSMENU
)
2181 PostMessageA( pmt
->hOwnerWnd
, WM_SYSCOMMAND
, item
->wID
,
2182 MAKELPARAM((INT16
)pmt
->pt
.x
, (INT16
)pmt
->pt
.y
) );
2184 PostMessageA( pmt
->hOwnerWnd
, WM_COMMAND
, item
->wID
, 0 );
2190 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hMenu
, TRUE
, wFlags
);
2195 /***********************************************************************
2196 * MENU_SwitchTracking
2198 * Helper function for menu navigation routines.
2200 static void MENU_SwitchTracking( MTRACKER
* pmt
, HMENU hPtMenu
, UINT id
)
2202 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2203 POPUPMENU
*topmenu
= MENU_GetMenu( pmt
->hTopMenu
);
2205 TRACE("%p hmenu=%p 0x%04x\n", pmt
, hPtMenu
, id
);
2207 if( pmt
->hTopMenu
!= hPtMenu
&&
2208 !((ptmenu
->wFlags
| topmenu
->wFlags
) & MF_POPUP
) )
2210 /* both are top level menus (system and menu-bar) */
2211 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2212 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2213 pmt
->hTopMenu
= hPtMenu
;
2215 else MENU_HideSubPopups( pmt
->hOwnerWnd
, hPtMenu
, FALSE
);
2216 MENU_SelectItem( pmt
->hOwnerWnd
, hPtMenu
, id
, TRUE
, 0 );
2220 /***********************************************************************
2223 * Return TRUE if we can go on with menu tracking.
2225 static BOOL
MENU_ButtonDown( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2227 TRACE("%p hPtMenu=%p\n", pmt
, hPtMenu
);
2232 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2235 if( IS_SYSTEM_MENU(ptmenu
) )
2236 item
= ptmenu
->items
;
2238 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2242 if( ptmenu
->FocusedItem
!= id
)
2243 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2245 /* If the popup menu is not already "popped" */
2246 if(!(item
->fState
& MF_MOUSESELECT
))
2248 pmt
->hCurrentMenu
= MENU_ShowSubPopup( pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2250 /* In win31, a newly popped menu always remains opened for the next buttonup */
2251 if(TWEAK_WineLook
== WIN31_LOOK
)
2252 ptmenu
->bTimeToHide
= FALSE
;
2257 /* Else the click was on the menu bar, finish the tracking */
2262 /***********************************************************************
2265 * Return the value of MENU_ExecFocusedItem if
2266 * the selected item was not a popup. Else open the popup.
2267 * A -1 return value indicates that we go on with menu tracking.
2270 static INT
MENU_ButtonUp( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2272 TRACE("%p hmenu=%p\n", pmt
, hPtMenu
);
2277 POPUPMENU
*ptmenu
= MENU_GetMenu( hPtMenu
);
2280 if( IS_SYSTEM_MENU(ptmenu
) )
2281 item
= ptmenu
->items
;
2283 item
= MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2285 if( item
&& (ptmenu
->FocusedItem
== id
))
2287 if( !(item
->fType
& MF_POPUP
) )
2288 return MENU_ExecFocusedItem( pmt
, hPtMenu
, wFlags
);
2290 /* If we are dealing with the top-level menu */
2291 /* and this is a click on an already "popped" item: */
2292 /* Stop the menu tracking and close the opened submenus */
2293 if((pmt
->hTopMenu
== hPtMenu
) && (ptmenu
->bTimeToHide
== TRUE
))
2296 ptmenu
->bTimeToHide
= TRUE
;
2302 /***********************************************************************
2305 * Return TRUE if we can go on with menu tracking.
2307 static BOOL
MENU_MouseMove( MTRACKER
* pmt
, HMENU hPtMenu
, UINT wFlags
)
2309 UINT id
= NO_SELECTED_ITEM
;
2310 POPUPMENU
*ptmenu
= NULL
;
2314 ptmenu
= MENU_GetMenu( hPtMenu
);
2315 if( IS_SYSTEM_MENU(ptmenu
) )
2318 MENU_FindItemByCoords( ptmenu
, pmt
->pt
, &id
);
2321 if( id
== NO_SELECTED_ITEM
)
2323 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2324 NO_SELECTED_ITEM
, TRUE
, pmt
->hTopMenu
);
2327 else if( ptmenu
->FocusedItem
!= id
)
2329 MENU_SwitchTracking( pmt
, hPtMenu
, id
);
2330 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hPtMenu
, FALSE
, wFlags
);
2336 /***********************************************************************
2339 static void MENU_SetCapture( HWND hwnd
)
2343 SERVER_START_REQ( set_capture_window
)
2346 req
->flags
= CAPTURE_MENU
;
2347 if (!wine_server_call_err( req
))
2349 previous
= reply
->previous
;
2350 hwnd
= reply
->full_handle
;
2355 if (previous
&& previous
!= hwnd
)
2356 SendMessageW( previous
, WM_CAPTURECHANGED
, 0, (LPARAM
)hwnd
);
2360 /***********************************************************************
2363 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2365 static LRESULT
MENU_DoNextMenu( MTRACKER
* pmt
, UINT vk
)
2367 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2369 if( (vk
== VK_LEFT
&& menu
->FocusedItem
== 0 ) ||
2370 (vk
== VK_RIGHT
&& menu
->FocusedItem
== menu
->nItems
- 1))
2372 MDINEXTMENU next_menu
;
2377 next_menu
.hmenuIn
= (IS_SYSTEM_MENU(menu
)) ? GetSubMenu(pmt
->hTopMenu
,0) : pmt
->hTopMenu
;
2378 next_menu
.hmenuNext
= 0;
2379 next_menu
.hwndNext
= 0;
2380 SendMessageW( pmt
->hOwnerWnd
, WM_NEXTMENU
, vk
, (LPARAM
)&next_menu
);
2382 TRACE("%p [%p] -> %p [%p]\n",
2383 pmt
->hCurrentMenu
, pmt
->hOwnerWnd
, next_menu
.hmenuNext
, next_menu
.hwndNext
);
2385 if (!next_menu
.hmenuNext
|| !next_menu
.hwndNext
)
2387 DWORD style
= GetWindowLongA( pmt
->hOwnerWnd
, GWL_STYLE
);
2388 hNewWnd
= pmt
->hOwnerWnd
;
2389 if( IS_SYSTEM_MENU(menu
) )
2391 /* switch to the menu bar */
2393 if(style
& WS_CHILD
|| !(hNewMenu
= GetMenu(hNewWnd
))) return FALSE
;
2397 menu
= MENU_GetMenu( hNewMenu
);
2398 id
= menu
->nItems
- 1;
2401 else if (style
& WS_SYSMENU
)
2403 /* switch to the system menu */
2404 hNewMenu
= get_win_sys_menu( hNewWnd
);
2408 else /* application returned a new menu to switch to */
2410 hNewMenu
= next_menu
.hmenuNext
;
2411 hNewWnd
= WIN_GetFullHandle( next_menu
.hwndNext
);
2413 if( IsMenu(hNewMenu
) && IsWindow(hNewWnd
) )
2415 DWORD style
= GetWindowLongA( hNewWnd
, GWL_STYLE
);
2417 if (style
& WS_SYSMENU
&&
2418 GetSubMenu(get_win_sys_menu(hNewWnd
), 0) == hNewMenu
)
2420 /* get the real system menu */
2421 hNewMenu
= get_win_sys_menu(hNewWnd
);
2423 else if (style
& WS_CHILD
|| GetMenu(hNewWnd
) != hNewMenu
)
2425 /* FIXME: Not sure what to do here;
2426 * perhaps try to track hNewMenu as a popup? */
2428 TRACE(" -- got confused.\n");
2435 if( hNewMenu
!= pmt
->hTopMenu
)
2437 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, NO_SELECTED_ITEM
,
2439 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2440 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2443 if( hNewWnd
!= pmt
->hOwnerWnd
)
2445 pmt
->hOwnerWnd
= hNewWnd
;
2446 MENU_SetCapture( pmt
->hOwnerWnd
);
2449 pmt
->hTopMenu
= pmt
->hCurrentMenu
= hNewMenu
; /* all subpopups are hidden */
2450 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hTopMenu
, id
, TRUE
, 0 );
2457 /***********************************************************************
2460 * The idea is not to show the popup if the next input message is
2461 * going to hide it anyway.
2463 static BOOL
MENU_SuspendPopup( MTRACKER
* pmt
, UINT16 uMsg
)
2467 msg
.hwnd
= pmt
->hOwnerWnd
;
2469 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2470 pmt
->trackFlags
|= TF_SKIPREMOVE
;
2475 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2476 if( msg
.message
== WM_KEYUP
|| msg
.message
== WM_PAINT
)
2478 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_REMOVE
);
2479 PeekMessageA( &msg
, 0, 0, 0, PM_NOYIELD
| PM_NOREMOVE
);
2480 if( msg
.message
== WM_KEYDOWN
&&
2481 (msg
.wParam
== VK_LEFT
|| msg
.wParam
== VK_RIGHT
))
2483 pmt
->trackFlags
|= TF_SUSPENDPOPUP
;
2490 /* failures go through this */
2491 pmt
->trackFlags
&= ~TF_SUSPENDPOPUP
;
2495 /***********************************************************************
2498 * Handle a VK_ESCAPE key event in a menu.
2500 static BOOL
MENU_KeyEscape(MTRACKER
* pmt
, UINT wFlags
)
2502 BOOL bEndMenu
= TRUE
;
2504 if (pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2506 POPUPMENU
*menu
= MENU_GetMenu(pmt
->hCurrentMenu
);
2508 if (menu
->wFlags
& MF_POPUP
)
2510 HMENU hmenutmp
, hmenuprev
;
2512 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2514 /* close topmost popup */
2515 while (hmenutmp
!= pmt
->hCurrentMenu
)
2517 hmenuprev
= hmenutmp
;
2518 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2521 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2522 pmt
->hCurrentMenu
= hmenuprev
;
2530 /***********************************************************************
2533 * Handle a VK_LEFT key event in a menu.
2535 static void MENU_KeyLeft( MTRACKER
* pmt
, UINT wFlags
)
2538 HMENU hmenutmp
, hmenuprev
;
2541 hmenuprev
= hmenutmp
= pmt
->hTopMenu
;
2542 menu
= MENU_GetMenu( hmenutmp
);
2544 /* Try to move 1 column left (if possible) */
2545 if( (prevcol
= MENU_GetStartOfPrevColumn( pmt
->hCurrentMenu
)) !=
2546 NO_SELECTED_ITEM
) {
2548 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2553 /* close topmost popup */
2554 while (hmenutmp
!= pmt
->hCurrentMenu
)
2556 hmenuprev
= hmenutmp
;
2557 hmenutmp
= MENU_GetSubPopup( hmenuprev
);
2560 MENU_HideSubPopups( pmt
->hOwnerWnd
, hmenuprev
, TRUE
);
2561 pmt
->hCurrentMenu
= hmenuprev
;
2563 if ( (hmenuprev
== pmt
->hTopMenu
) && !(menu
->wFlags
& MF_POPUP
) )
2565 /* move menu bar selection if no more popups are left */
2567 if( !MENU_DoNextMenu( pmt
, VK_LEFT
) )
2568 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_PREV
);
2570 if ( hmenuprev
!= hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2572 /* A sublevel menu was displayed - display the next one
2573 * unless there is another displacement coming up */
2575 if( !MENU_SuspendPopup( pmt
, WM_KEYDOWN
) )
2576 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2577 pmt
->hTopMenu
, TRUE
, wFlags
);
2583 /***********************************************************************
2586 * Handle a VK_RIGHT key event in a menu.
2588 static void MENU_KeyRight( MTRACKER
* pmt
, UINT wFlags
)
2591 POPUPMENU
*menu
= MENU_GetMenu( pmt
->hTopMenu
);
2594 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2596 debugstr_w((MENU_GetMenu(pmt
->hCurrentMenu
))->items
[0].text
),
2597 pmt
->hTopMenu
, debugstr_w(menu
->items
[0].text
) );
2599 if ( (menu
->wFlags
& MF_POPUP
) || (pmt
->hCurrentMenu
!= pmt
->hTopMenu
))
2601 /* If already displaying a popup, try to display sub-popup */
2603 hmenutmp
= pmt
->hCurrentMenu
;
2604 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
, hmenutmp
, TRUE
, wFlags
);
2606 /* if subpopup was displayed then we are done */
2607 if (hmenutmp
!= pmt
->hCurrentMenu
) return;
2610 /* Check to see if there's another column */
2611 if( (nextcol
= MENU_GetStartOfNextColumn( pmt
->hCurrentMenu
)) !=
2612 NO_SELECTED_ITEM
) {
2613 TRACE("Going to %d.\n", nextcol
);
2614 MENU_SelectItem( pmt
->hOwnerWnd
, pmt
->hCurrentMenu
,
2619 if (!(menu
->wFlags
& MF_POPUP
)) /* menu bar tracking */
2621 if( pmt
->hCurrentMenu
!= pmt
->hTopMenu
)
2623 MENU_HideSubPopups( pmt
->hOwnerWnd
, pmt
->hTopMenu
, FALSE
);
2624 hmenutmp
= pmt
->hCurrentMenu
= pmt
->hTopMenu
;
2625 } else hmenutmp
= 0;
2627 /* try to move to the next item */
2628 if( !MENU_DoNextMenu( pmt
, VK_RIGHT
) )
2629 MENU_MoveSelection( pmt
->hOwnerWnd
, pmt
->hTopMenu
, ITEM_NEXT
);
2631 if( hmenutmp
|| pmt
->trackFlags
& TF_SUSPENDPOPUP
)
2632 if( !MENU_SuspendPopup(pmt
, WM_KEYDOWN
) )
2633 pmt
->hCurrentMenu
= MENU_ShowSubPopup(pmt
->hOwnerWnd
,
2634 pmt
->hTopMenu
, TRUE
, wFlags
);
2638 /***********************************************************************
2641 * Menu tracking code.
2643 static INT
MENU_TrackMenu( HMENU hmenu
, UINT wFlags
, INT x
, INT y
,
2644 HWND hwnd
, const RECT
*lprect
)
2649 INT executedMenuId
= -1;
2651 BOOL enterIdleSent
= FALSE
;
2654 mt
.hCurrentMenu
= hmenu
;
2655 mt
.hTopMenu
= hmenu
;
2656 mt
.hOwnerWnd
= WIN_GetFullHandle( hwnd
);
2660 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p (%ld,%ld)-(%ld,%ld)\n",
2661 hmenu
, wFlags
, x
, y
, hwnd
, (lprect
) ? lprect
->left
: 0, (lprect
) ? lprect
->top
: 0,
2662 (lprect
) ? lprect
->right
: 0, (lprect
) ? lprect
->bottom
: 0);
2665 if (!(menu
= MENU_GetMenu( hmenu
))) return FALSE
;
2667 if (wFlags
& TPM_BUTTONDOWN
)
2669 /* Get the result in order to start the tracking or not */
2670 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2671 fEndMenu
= !fRemove
;
2674 MENU_SetCapture( mt
.hOwnerWnd
);
2678 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2679 if (!menu
) /* sometimes happens if I do a window manager close */
2682 /* we have to keep the message in the queue until it's
2683 * clear that menu loop is not over yet. */
2687 if (PeekMessageA( &msg
, 0, 0, 0, PM_NOREMOVE
))
2689 if (!CallMsgFilterA( &msg
, MSGF_MENU
)) break;
2690 /* remove the message from the queue */
2691 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2697 HWND win
= (wFlags
& TPM_ENTERIDLEEX
&& menu
->wFlags
& MF_POPUP
) ? menu
->hWnd
: 0;
2698 enterIdleSent
= TRUE
;
2699 SendMessageW( mt
.hOwnerWnd
, WM_ENTERIDLE
, MSGF_MENU
, (LPARAM
)win
);
2705 /* check if EndMenu() tried to cancel us, by posting this message */
2706 if(msg
.message
== WM_CANCELMODE
)
2708 /* we are now out of the loop */
2711 /* remove the message from the queue */
2712 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2714 /* break out of internal loop, ala ESCAPE */
2718 TranslateMessage( &msg
);
2721 if ( (msg
.hwnd
==menu
->hWnd
) || (msg
.message
!=WM_TIMER
) )
2722 enterIdleSent
=FALSE
;
2725 if ((msg
.message
>= WM_MOUSEFIRST
) && (msg
.message
<= WM_MOUSELAST
))
2728 * Use the mouse coordinates in lParam instead of those in the MSG
2729 * struct to properly handle synthetic messages. They are already
2730 * in screen coordinates.
2732 mt
.pt
.x
= (short)LOWORD(msg
.lParam
);
2733 mt
.pt
.y
= (short)HIWORD(msg
.lParam
);
2735 /* Find a menu for this mouse event */
2736 hmenu
= MENU_PtMenu( mt
.hTopMenu
, mt
.pt
);
2740 /* no WM_NC... messages in captured state */
2742 case WM_RBUTTONDBLCLK
:
2743 case WM_RBUTTONDOWN
:
2744 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2746 case WM_LBUTTONDBLCLK
:
2747 case WM_LBUTTONDOWN
:
2748 /* If the message belongs to the menu, removes it from the queue */
2749 /* Else, end menu tracking */
2750 fRemove
= MENU_ButtonDown( &mt
, hmenu
, wFlags
);
2751 fEndMenu
= !fRemove
;
2755 if (!(wFlags
& TPM_RIGHTBUTTON
)) break;
2758 /* Check if a menu was selected by the mouse */
2761 executedMenuId
= MENU_ButtonUp( &mt
, hmenu
, wFlags
);
2763 /* End the loop if executedMenuId is an item ID */
2764 /* or if the job was done (executedMenuId = 0). */
2765 fEndMenu
= fRemove
= (executedMenuId
!= -1);
2767 /* No menu was selected by the mouse */
2768 /* if the function was called by TrackPopupMenu, continue
2769 with the menu tracking. If not, stop it */
2771 fEndMenu
= ((wFlags
& TPM_POPUPMENU
) ? FALSE
: TRUE
);
2776 /* In win95 winelook, the selected menu item must be changed every time the
2777 mouse moves. In Win31 winelook, the mouse button has to be held down */
2779 if ( hmenu
&& ((TWEAK_WineLook
> WIN31_LOOK
) ||
2780 ( (msg
.wParam
& MK_LBUTTON
) ||
2781 ((wFlags
& TPM_RIGHTBUTTON
) && (msg
.wParam
& MK_RBUTTON
)))) )
2783 fEndMenu
|= !MENU_MouseMove( &mt
, hmenu
, wFlags
);
2785 } /* switch(msg.message) - mouse */
2787 else if ((msg
.message
>= WM_KEYFIRST
) && (msg
.message
<= WM_KEYLAST
))
2789 fRemove
= TRUE
; /* Keyboard messages are always removed */
2797 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2798 NO_SELECTED_ITEM
, FALSE
, 0 );
2801 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2802 (msg
.wParam
== VK_HOME
)? ITEM_NEXT
: ITEM_PREV
);
2805 case VK_DOWN
: /* If on menu bar, pull-down the menu */
2807 menu
= MENU_GetMenu( mt
.hCurrentMenu
);
2808 if (!(menu
->wFlags
& MF_POPUP
))
2809 mt
.hCurrentMenu
= MENU_ShowSubPopup(mt
.hOwnerWnd
, mt
.hTopMenu
, TRUE
, wFlags
);
2810 else /* otherwise try to move selection */
2811 MENU_MoveSelection( mt
.hOwnerWnd
, mt
.hCurrentMenu
, ITEM_NEXT
);
2815 MENU_KeyLeft( &mt
, wFlags
);
2819 MENU_KeyRight( &mt
, wFlags
);
2823 fEndMenu
= MENU_KeyEscape(&mt
, wFlags
);
2829 hi
.cbSize
= sizeof(HELPINFO
);
2830 hi
.iContextType
= HELPINFO_MENUITEM
;
2831 if (menu
->FocusedItem
== NO_SELECTED_ITEM
)
2834 hi
.iCtrlId
= menu
->items
[menu
->FocusedItem
].wID
;
2835 hi
.hItemHandle
= hmenu
;
2836 hi
.dwContextId
= menu
->dwContextHelpID
;
2837 hi
.MousePos
= msg
.pt
;
2838 SendMessageA(hwnd
, WM_HELP
, 0, (LPARAM
)&hi
);
2845 break; /* WM_KEYDOWN */
2855 break; /* WM_SYSKEYDOWN */
2861 if (msg
.wParam
== '\r' || msg
.wParam
== ' ')
2863 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2864 fEndMenu
= (executedMenuId
!= -1);
2869 /* Hack to avoid control chars. */
2870 /* We will find a better way real soon... */
2871 if ((msg
.wParam
<= 32) || (msg
.wParam
>= 127)) break;
2873 pos
= MENU_FindItemByKey( mt
.hOwnerWnd
, mt
.hCurrentMenu
,
2874 LOWORD(msg
.wParam
), FALSE
);
2875 if (pos
== (UINT
)-2) fEndMenu
= TRUE
;
2876 else if (pos
== (UINT
)-1) MessageBeep(0);
2879 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hCurrentMenu
, pos
,
2881 executedMenuId
= MENU_ExecFocusedItem(&mt
,mt
.hCurrentMenu
, wFlags
);
2882 fEndMenu
= (executedMenuId
!= -1);
2886 } /* switch(msg.message) - kbd */
2890 DispatchMessageA( &msg
);
2893 if (!fEndMenu
) fRemove
= TRUE
;
2895 /* finally remove message from the queue */
2897 if (fRemove
&& !(mt
.trackFlags
& TF_SKIPREMOVE
) )
2898 PeekMessageA( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
2899 else mt
.trackFlags
&= ~TF_SKIPREMOVE
;
2902 MENU_SetCapture(0); /* release the capture */
2904 /* If dropdown is still painted and the close box is clicked on
2905 then the menu will be destroyed as part of the DispatchMessage above.
2906 This will then invalidate the menu handle in mt.hTopMenu. We should
2907 check for this first. */
2908 if( IsMenu( mt
.hTopMenu
) )
2910 menu
= MENU_GetMenu( mt
.hTopMenu
);
2912 if( IsWindow( mt
.hOwnerWnd
) )
2914 MENU_HideSubPopups( mt
.hOwnerWnd
, mt
.hTopMenu
, FALSE
);
2916 if (menu
&& menu
->wFlags
& MF_POPUP
)
2918 DestroyWindow( menu
->hWnd
);
2921 MENU_SelectItem( mt
.hOwnerWnd
, mt
.hTopMenu
, NO_SELECTED_ITEM
, FALSE
, 0 );
2922 SendMessageA( mt
.hOwnerWnd
, WM_MENUSELECT
, MAKELONG(0,0xffff), 0 );
2925 /* Reset the variable for hiding menu */
2926 if( menu
) menu
->bTimeToHide
= FALSE
;
2929 /* The return value is only used by TrackPopupMenu */
2930 return ((executedMenuId
!= -1) ? executedMenuId
: 0);
2933 /***********************************************************************
2936 static BOOL
MENU_InitTracking(HWND hWnd
, HMENU hMenu
, BOOL bPopup
, UINT wFlags
)
2938 TRACE("hwnd=%p hmenu=%p\n", hWnd
, hMenu
);
2942 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
2943 if (!(wFlags
& TPM_NONOTIFY
))
2944 SendMessageA( hWnd
, WM_ENTERMENULOOP
, bPopup
, 0 );
2946 SendMessageA( hWnd
, WM_SETCURSOR
, (WPARAM
)hWnd
, HTCAPTION
);
2948 if (!(wFlags
& TPM_NONOTIFY
))
2951 SendMessageA( hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0 );
2952 if ((menu
= MENU_GetMenu( hMenu
)) && (!menu
->Height
))
2953 { /* app changed/recreated menu bar entries in WM_INITMENU
2954 Recalculate menu sizes else clicks will not work */
2955 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
2956 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
2962 /***********************************************************************
2965 static BOOL
MENU_ExitTracking(HWND hWnd
)
2967 TRACE("hwnd=%p\n", hWnd
);
2969 SendMessageA( hWnd
, WM_EXITMENULOOP
, 0, 0 );
2974 /***********************************************************************
2975 * MENU_TrackMouseMenuBar
2977 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
2979 void MENU_TrackMouseMenuBar( HWND hWnd
, INT ht
, POINT pt
)
2981 HMENU hMenu
= (ht
== HTSYSMENU
) ? get_win_sys_menu( hWnd
) : GetMenu( hWnd
);
2982 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_BUTTONDOWN
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
2984 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd
, ht
, pt
.x
, pt
.y
);
2988 /* map point to parent client coordinates */
2989 HWND parent
= GetAncestor( hWnd
, GA_PARENT
);
2990 if (parent
!= GetDesktopWindow()) ScreenToClient( parent
, &pt
);
2992 MENU_InitTracking( hWnd
, hMenu
, FALSE
, wFlags
);
2993 MENU_TrackMenu( hMenu
, wFlags
, pt
.x
, pt
.y
, hWnd
, NULL
);
2994 MENU_ExitTracking(hWnd
);
2999 /***********************************************************************
3000 * MENU_TrackKbdMenuBar
3002 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3004 void MENU_TrackKbdMenuBar( HWND hwnd
, UINT wParam
, INT vkey
)
3006 UINT uItem
= NO_SELECTED_ITEM
;
3008 UINT wFlags
= TPM_ENTERIDLEEX
| TPM_LEFTALIGN
| TPM_LEFTBUTTON
;
3010 /* find window that has a menu */
3012 while (GetWindowLongA( hwnd
, GWL_STYLE
) & WS_CHILD
)
3013 if (!(hwnd
= GetParent( hwnd
))) return;
3015 /* check if we have to track a system menu */
3017 hTrackMenu
= GetMenu( hwnd
);
3018 if (!hTrackMenu
|| IsIconic(hwnd
) || vkey
== VK_SPACE
)
3020 if (!(GetWindowLongA( hwnd
, GWL_STYLE
) & WS_SYSMENU
)) return;
3021 if (GetWindowLongA( hwnd
, GWL_EXSTYLE
) & WS_EX_MANAGED
) return;
3022 hTrackMenu
= get_win_sys_menu( hwnd
);
3024 wParam
|= HTSYSMENU
; /* prevent item lookup */
3027 if (!IsMenu( hTrackMenu
)) return;
3029 MENU_InitTracking( hwnd
, hTrackMenu
, FALSE
, wFlags
);
3031 if( vkey
&& vkey
!= VK_SPACE
)
3033 uItem
= MENU_FindItemByKey( hwnd
, hTrackMenu
, vkey
, (wParam
& HTSYSMENU
) );
3034 if( uItem
>= (UINT
)(-2) )
3036 if( uItem
== (UINT
)(-1) ) MessageBeep(0);
3043 MENU_SelectItem( hwnd
, hTrackMenu
, uItem
, TRUE
, 0 );
3045 if( uItem
== NO_SELECTED_ITEM
)
3046 MENU_MoveSelection( hwnd
, hTrackMenu
, ITEM_NEXT
);
3048 PostMessageA( hwnd
, WM_KEYDOWN
, VK_DOWN
, 0L );
3050 MENU_TrackMenu( hTrackMenu
, wFlags
, 0, 0, hwnd
, NULL
);
3052 MENU_ExitTracking( hwnd
);
3056 /**********************************************************************
3057 * TrackPopupMenu (USER32.@)
3059 * Like the win32 API, the function return the command ID only if the
3060 * flag TPM_RETURNCMD is on.
3063 BOOL WINAPI
TrackPopupMenu( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3064 INT nReserved
, HWND hWnd
, const RECT
*lpRect
)
3068 MENU_InitTracking(hWnd
, hMenu
, TRUE
, wFlags
);
3070 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3071 if (!(wFlags
& TPM_NONOTIFY
))
3072 SendMessageA( hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hMenu
, 0);
3074 if (MENU_ShowPopup( hWnd
, hMenu
, 0, x
, y
, 0, 0 ))
3075 ret
= MENU_TrackMenu( hMenu
, wFlags
| TPM_POPUPMENU
, 0, 0, hWnd
, lpRect
);
3076 MENU_ExitTracking(hWnd
);
3078 if( (!(wFlags
& TPM_RETURNCMD
)) && (ret
!= FALSE
) )
3084 /**********************************************************************
3085 * TrackPopupMenuEx (USER32.@)
3087 BOOL WINAPI
TrackPopupMenuEx( HMENU hMenu
, UINT wFlags
, INT x
, INT y
,
3088 HWND hWnd
, LPTPMPARAMS lpTpm
)
3090 FIXME("not fully implemented\n" );
3091 return TrackPopupMenu( hMenu
, wFlags
, x
, y
, 0, hWnd
,
3092 lpTpm
? &lpTpm
->rcExclude
: NULL
);
3095 /***********************************************************************
3098 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3100 static LRESULT WINAPI
PopupMenuWndProc( HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
3102 TRACE("hwnd=%p msg=0x%04x wp=0x%04x lp=0x%08lx\n", hwnd
, message
, wParam
, lParam
);
3108 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)lParam
;
3109 SetWindowLongW( hwnd
, 0, (LONG
)cs
->lpCreateParams
);
3113 case WM_MOUSEACTIVATE
: /* We don't want to be activated */
3114 return MA_NOACTIVATE
;
3119 BeginPaint( hwnd
, &ps
);
3120 MENU_DrawPopupMenu( hwnd
, ps
.hdc
,
3121 (HMENU
)GetWindowLongA( hwnd
, 0 ) );
3122 EndPaint( hwnd
, &ps
);
3129 /* zero out global pointer in case resident popup window was destroyed. */
3130 if (hwnd
== top_popup
) top_popup
= 0;
3137 if (!GetWindowLongW( hwnd
, 0 )) ERR("no menu to display\n");
3140 SetWindowLongW( hwnd
, 0, 0 );
3143 case MM_SETMENUHANDLE
:
3144 SetWindowLongW( hwnd
, 0, wParam
);
3147 case MM_GETMENUHANDLE
:
3148 return GetWindowLongW( hwnd
, 0 );
3151 return DefWindowProcW( hwnd
, message
, wParam
, lParam
);
3157 /***********************************************************************
3158 * MENU_GetMenuBarHeight
3160 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3162 UINT
MENU_GetMenuBarHeight( HWND hwnd
, UINT menubarWidth
,
3163 INT orgX
, INT orgY
)
3169 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd
, menubarWidth
, orgX
, orgY
);
3171 if (!(lppop
= MENU_GetMenu( GetMenu(hwnd
) ))) return 0;
3173 hdc
= GetDCEx( hwnd
, 0, DCX_CACHE
| DCX_WINDOW
);
3174 SelectObject( hdc
, hMenuFont
);
3175 SetRect(&rectBar
, orgX
, orgY
, orgX
+menubarWidth
, orgY
+GetSystemMetrics(SM_CYMENU
));
3176 MENU_MenuBarCalcSize( hdc
, &rectBar
, lppop
, hwnd
);
3177 ReleaseDC( hwnd
, hdc
);
3178 return lppop
->Height
;
3182 /*******************************************************************
3183 * ChangeMenuA (USER32.@)
3185 BOOL WINAPI
ChangeMenuA( HMENU hMenu
, UINT pos
, LPCSTR data
,
3186 UINT id
, UINT flags
)
3188 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3189 if (flags
& MF_APPEND
) return AppendMenuA( hMenu
, flags
& ~MF_APPEND
,
3191 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3192 if (flags
& MF_CHANGE
) return ModifyMenuA(hMenu
, pos
, flags
& ~MF_CHANGE
,
3194 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3195 flags
& MF_BYPOSITION
? pos
: id
,
3196 flags
& ~MF_REMOVE
);
3197 /* Default: MF_INSERT */
3198 return InsertMenuA( hMenu
, pos
, flags
, id
, data
);
3202 /*******************************************************************
3203 * ChangeMenuW (USER32.@)
3205 BOOL WINAPI
ChangeMenuW( HMENU hMenu
, UINT pos
, LPCWSTR data
,
3206 UINT id
, UINT flags
)
3208 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu
, pos
, data
, id
, flags
);
3209 if (flags
& MF_APPEND
) return AppendMenuW( hMenu
, flags
& ~MF_APPEND
,
3211 if (flags
& MF_DELETE
) return DeleteMenu(hMenu
, pos
, flags
& ~MF_DELETE
);
3212 if (flags
& MF_CHANGE
) return ModifyMenuW(hMenu
, pos
, flags
& ~MF_CHANGE
,
3214 if (flags
& MF_REMOVE
) return RemoveMenu( hMenu
,
3215 flags
& MF_BYPOSITION
? pos
: id
,
3216 flags
& ~MF_REMOVE
);
3217 /* Default: MF_INSERT */
3218 return InsertMenuW( hMenu
, pos
, flags
, id
, data
);
3222 /*******************************************************************
3223 * CheckMenuItem (USER32.@)
3225 DWORD WINAPI
CheckMenuItem( HMENU hMenu
, UINT id
, UINT flags
)
3230 TRACE("menu=%p id=%04x flags=%04x\n", hMenu
, id
, flags
);
3231 if (!(item
= MENU_FindItem( &hMenu
, &id
, flags
))) return -1;
3232 ret
= item
->fState
& MF_CHECKED
;
3233 if (flags
& MF_CHECKED
) item
->fState
|= MF_CHECKED
;
3234 else item
->fState
&= ~MF_CHECKED
;
3239 /**********************************************************************
3240 * EnableMenuItem (USER32.@)
3242 UINT WINAPI
EnableMenuItem( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3248 TRACE("(%p, %04x, %04x) !\n", hMenu
, wItemID
, wFlags
);
3250 /* Get the Popupmenu to access the owner menu */
3251 if (!(menu
= MENU_GetMenu(hMenu
)))
3254 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
)))
3257 oldflags
= item
->fState
& (MF_GRAYED
| MF_DISABLED
);
3258 item
->fState
^= (oldflags
^ wFlags
) & (MF_GRAYED
| MF_DISABLED
);
3260 /* In win95 if the close item in the system menu change update the close button */
3261 if (TWEAK_WineLook
== WIN95_LOOK
)
3262 if((item
->wID
== SC_CLOSE
) && (oldflags
!= wFlags
))
3264 if (menu
->hSysMenuOwner
!= 0)
3266 POPUPMENU
* parentMenu
;
3268 /* Get the parent menu to access*/
3269 if (!(parentMenu
= MENU_GetMenu(menu
->hSysMenuOwner
)))
3272 /* Refresh the frame to reflect the change*/
3273 SetWindowPos(parentMenu
->hWnd
, 0, 0, 0, 0, 0,
3274 SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
);
3282 /*******************************************************************
3283 * GetMenuStringA (USER32.@)
3285 INT WINAPI
GetMenuStringA(
3286 HMENU hMenu
, /* [in] menuhandle */
3287 UINT wItemID
, /* [in] menu item (dep. on wFlags) */
3288 LPSTR str
, /* [out] outbuffer. If NULL, func returns entry length*/
3289 INT nMaxSiz
, /* [in] length of buffer. if 0, func returns entry len*/
3290 UINT wFlags
/* [in] MF_ flags */
3294 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3295 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3296 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3297 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3299 if (!WideCharToMultiByte( CP_ACP
, 0, item
->text
, -1, str
, nMaxSiz
, NULL
, NULL
))
3301 TRACE("returning '%s'\n", str
);
3306 /*******************************************************************
3307 * GetMenuStringW (USER32.@)
3309 INT WINAPI
GetMenuStringW( HMENU hMenu
, UINT wItemID
,
3310 LPWSTR str
, INT nMaxSiz
, UINT wFlags
)
3314 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu
, wItemID
, str
, nMaxSiz
, wFlags
);
3315 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return 0;
3316 if (!IS_STRING_ITEM(item
->fType
)) return 0;
3317 if (!str
|| !nMaxSiz
) return strlenW(item
->text
);
3319 lstrcpynW( str
, item
->text
, nMaxSiz
);
3320 return strlenW(str
);
3324 /**********************************************************************
3325 * HiliteMenuItem (USER32.@)
3327 BOOL WINAPI
HiliteMenuItem( HWND hWnd
, HMENU hMenu
, UINT wItemID
,
3331 TRACE("(%p, %p, %04x, %04x);\n", hWnd
, hMenu
, wItemID
, wHilite
);
3332 if (!MENU_FindItem( &hMenu
, &wItemID
, wHilite
)) return FALSE
;
3333 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3334 if (menu
->FocusedItem
== wItemID
) return TRUE
;
3335 MENU_HideSubPopups( hWnd
, hMenu
, FALSE
);
3336 MENU_SelectItem( hWnd
, hMenu
, wItemID
, TRUE
, 0 );
3341 /**********************************************************************
3342 * GetMenuState (USER32.@)
3344 UINT WINAPI
GetMenuState( HMENU hMenu
, UINT wItemID
, UINT wFlags
)
3347 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu
, wItemID
, wFlags
);
3348 if (!(item
= MENU_FindItem( &hMenu
, &wItemID
, wFlags
))) return -1;
3349 debug_print_menuitem (" item: ", item
, "");
3350 if (item
->fType
& MF_POPUP
)
3352 POPUPMENU
*menu
= MENU_GetMenu( item
->hSubMenu
);
3353 if (!menu
) return -1;
3354 else return (menu
->nItems
<< 8) | ((item
->fState
|item
->fType
) & 0xff);
3358 /* We used to (from way back then) mask the result to 0xff. */
3359 /* I don't know why and it seems wrong as the documented */
3360 /* return flag MF_SEPARATOR is outside that mask. */
3361 return (item
->fType
| item
->fState
);
3366 /**********************************************************************
3367 * GetMenuItemCount (USER32.@)
3369 INT WINAPI
GetMenuItemCount( HMENU hMenu
)
3371 LPPOPUPMENU menu
= MENU_GetMenu(hMenu
);
3372 if (!menu
) return -1;
3373 TRACE("(%p) returning %d\n", hMenu
, menu
->nItems
);
3374 return menu
->nItems
;
3378 /**********************************************************************
3379 * GetMenuItemID (USER32.@)
3381 UINT WINAPI
GetMenuItemID( HMENU hMenu
, INT nPos
)
3385 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return -1;
3386 if (lpmi
->fType
& MF_POPUP
) return -1;
3392 /*******************************************************************
3393 * InsertMenuW (USER32.@)
3395 BOOL WINAPI
InsertMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3396 UINT_PTR id
, LPCWSTR str
)
3400 if (IS_STRING_ITEM(flags
) && str
)
3401 TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %s\n",
3402 hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3403 else TRACE("hMenu %p, pos %d, flags %08x, id %04x, str %08lx (not a string)\n",
3404 hMenu
, pos
, flags
, id
, (DWORD
)str
);
3406 if (!(item
= MENU_InsertItem( hMenu
, pos
, flags
))) return FALSE
;
3408 if (!(MENU_SetItemData( item
, flags
, id
, str
)))
3410 RemoveMenu( hMenu
, pos
, flags
);
3414 if (flags
& MF_POPUP
) /* Set the MF_POPUP flag on the popup-menu */
3415 (MENU_GetMenu((HMENU
)id
))->wFlags
|= MF_POPUP
;
3417 item
->hCheckBit
= item
->hUnCheckBit
= 0;
3422 /*******************************************************************
3423 * InsertMenuA (USER32.@)
3425 BOOL WINAPI
InsertMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3426 UINT_PTR id
, LPCSTR str
)
3430 if (IS_STRING_ITEM(flags
) && str
)
3432 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3433 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3436 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3437 ret
= InsertMenuW( hMenu
, pos
, flags
, id
, newstr
);
3438 HeapFree( GetProcessHeap(), 0, newstr
);
3442 else return InsertMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3446 /*******************************************************************
3447 * AppendMenuA (USER32.@)
3449 BOOL WINAPI
AppendMenuA( HMENU hMenu
, UINT flags
,
3450 UINT_PTR id
, LPCSTR data
)
3452 return InsertMenuA( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3456 /*******************************************************************
3457 * AppendMenuW (USER32.@)
3459 BOOL WINAPI
AppendMenuW( HMENU hMenu
, UINT flags
,
3460 UINT_PTR id
, LPCWSTR data
)
3462 return InsertMenuW( hMenu
, -1, flags
| MF_BYPOSITION
, id
, data
);
3466 /**********************************************************************
3467 * RemoveMenu (USER32.@)
3469 BOOL WINAPI
RemoveMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3474 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu
, nPos
, wFlags
);
3475 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3476 if (!(menu
= MENU_GetMenu(hMenu
))) return FALSE
;
3480 MENU_FreeItemData( item
);
3482 if (--menu
->nItems
== 0)
3484 HeapFree( GetProcessHeap(), 0, menu
->items
);
3489 while(nPos
< menu
->nItems
)
3495 menu
->items
= HeapReAlloc( GetProcessHeap(), 0, menu
->items
,
3496 menu
->nItems
* sizeof(MENUITEM
) );
3502 /**********************************************************************
3503 * DeleteMenu (USER32.@)
3505 BOOL WINAPI
DeleteMenu( HMENU hMenu
, UINT nPos
, UINT wFlags
)
3507 MENUITEM
*item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
);
3508 if (!item
) return FALSE
;
3509 if (item
->fType
& MF_POPUP
) DestroyMenu( item
->hSubMenu
);
3510 /* nPos is now the position of the item */
3511 RemoveMenu( hMenu
, nPos
, wFlags
| MF_BYPOSITION
);
3516 /*******************************************************************
3517 * ModifyMenuW (USER32.@)
3519 BOOL WINAPI
ModifyMenuW( HMENU hMenu
, UINT pos
, UINT flags
,
3520 UINT_PTR id
, LPCWSTR str
)
3524 if (IS_STRING_ITEM(flags
))
3526 TRACE("%p %d %04x %04x %s\n", hMenu
, pos
, flags
, id
, debugstr_w(str
) );
3530 TRACE("%p %d %04x %04x %08lx\n", hMenu
, pos
, flags
, id
, (DWORD
)str
);
3533 if (!(item
= MENU_FindItem( &hMenu
, &pos
, flags
))) return FALSE
;
3534 return MENU_SetItemData( item
, flags
, id
, str
);
3538 /*******************************************************************
3539 * ModifyMenuA (USER32.@)
3541 BOOL WINAPI
ModifyMenuA( HMENU hMenu
, UINT pos
, UINT flags
,
3542 UINT_PTR id
, LPCSTR str
)
3546 if (IS_STRING_ITEM(flags
) && str
)
3548 INT len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
3549 LPWSTR newstr
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
3552 MultiByteToWideChar( CP_ACP
, 0, str
, -1, newstr
, len
);
3553 ret
= ModifyMenuW( hMenu
, pos
, flags
, id
, newstr
);
3554 HeapFree( GetProcessHeap(), 0, newstr
);
3558 else return ModifyMenuW( hMenu
, pos
, flags
, id
, (LPCWSTR
)str
);
3562 /**********************************************************************
3563 * CreatePopupMenu (USER32.@)
3565 HMENU WINAPI
CreatePopupMenu(void)
3570 if (!(hmenu
= CreateMenu())) return 0;
3571 menu
= MENU_GetMenu( hmenu
);
3572 menu
->wFlags
|= MF_POPUP
;
3573 menu
->bTimeToHide
= FALSE
;
3578 /**********************************************************************
3579 * GetMenuCheckMarkDimensions (USER.417)
3580 * GetMenuCheckMarkDimensions (USER32.@)
3582 DWORD WINAPI
GetMenuCheckMarkDimensions(void)
3584 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK
), GetSystemMetrics(SM_CYMENUCHECK
) );
3588 /**********************************************************************
3589 * SetMenuItemBitmaps (USER32.@)
3591 BOOL WINAPI
SetMenuItemBitmaps( HMENU hMenu
, UINT nPos
, UINT wFlags
,
3592 HBITMAP hNewUnCheck
, HBITMAP hNewCheck
)
3595 TRACE("(%p, %04x, %04x, %p, %p)\n",
3596 hMenu
, nPos
, wFlags
, hNewCheck
, hNewUnCheck
);
3597 if (!(item
= MENU_FindItem( &hMenu
, &nPos
, wFlags
))) return FALSE
;
3599 if (!hNewCheck
&& !hNewUnCheck
)
3601 item
->fState
&= ~MF_USECHECKBITMAPS
;
3603 else /* Install new bitmaps */
3605 item
->hCheckBit
= hNewCheck
;
3606 item
->hUnCheckBit
= hNewUnCheck
;
3607 item
->fState
|= MF_USECHECKBITMAPS
;
3613 /**********************************************************************
3614 * CreateMenu (USER32.@)
3616 HMENU WINAPI
CreateMenu(void)
3620 if (!(hMenu
= USER_HEAP_ALLOC( sizeof(POPUPMENU
) ))) return 0;
3621 menu
= (LPPOPUPMENU
) USER_HEAP_LIN_ADDR(hMenu
);
3623 ZeroMemory(menu
, sizeof(POPUPMENU
));
3624 menu
->wMagic
= MENU_MAGIC
;
3625 menu
->FocusedItem
= NO_SELECTED_ITEM
;
3626 menu
->bTimeToHide
= FALSE
;
3628 TRACE("return %p\n", hMenu
);
3634 /**********************************************************************
3635 * DestroyMenu (USER32.@)
3637 BOOL WINAPI
DestroyMenu( HMENU hMenu
)
3639 TRACE("(%p)\n", hMenu
);
3641 /* Silently ignore attempts to destroy default system popup */
3643 if (hMenu
&& hMenu
!= MENU_DefSysPopup
)
3645 LPPOPUPMENU lppop
= MENU_GetMenu(hMenu
);
3647 if (!lppop
) return FALSE
;
3649 lppop
->wMagic
= 0; /* Mark it as destroyed */
3651 if ((lppop
->wFlags
& MF_POPUP
) && lppop
->hWnd
)
3653 DestroyWindow( lppop
->hWnd
);
3657 if (lppop
->items
) /* recursively destroy submenus */
3660 MENUITEM
*item
= lppop
->items
;
3661 for (i
= lppop
->nItems
; i
> 0; i
--, item
++)
3663 if (item
->fType
& MF_POPUP
) DestroyMenu(item
->hSubMenu
);
3664 MENU_FreeItemData( item
);
3666 HeapFree( GetProcessHeap(), 0, lppop
->items
);
3668 USER_HEAP_FREE( hMenu
);
3670 return (hMenu
!= MENU_DefSysPopup
);
3674 /**********************************************************************
3675 * GetSystemMenu (USER32.@)
3677 HMENU WINAPI
GetSystemMenu( HWND hWnd
, BOOL bRevert
)
3679 WND
*wndPtr
= WIN_FindWndPtr( hWnd
);
3684 if( wndPtr
->hSysMenu
)
3688 DestroyMenu(wndPtr
->hSysMenu
);
3689 wndPtr
->hSysMenu
= 0;
3693 POPUPMENU
*menu
= MENU_GetMenu( wndPtr
->hSysMenu
);
3696 if( menu
->nItems
> 0 && menu
->items
[0].hSubMenu
== MENU_DefSysPopup
)
3697 menu
->items
[0].hSubMenu
= MENU_CopySysPopup();
3701 WARN("Current sys-menu (%p) of wnd %p is broken\n",
3702 wndPtr
->hSysMenu
, hWnd
);
3703 wndPtr
->hSysMenu
= 0;
3708 if(!wndPtr
->hSysMenu
&& (wndPtr
->dwStyle
& WS_SYSMENU
) )
3709 wndPtr
->hSysMenu
= MENU_GetSysMenu( hWnd
, (HMENU
)(-1) );
3711 if( wndPtr
->hSysMenu
)
3714 retvalue
= GetSubMenu(wndPtr
->hSysMenu
, 0);
3716 /* Store the dummy sysmenu handle to facilitate the refresh */
3717 /* of the close button if the SC_CLOSE item change */
3718 menu
= MENU_GetMenu(retvalue
);
3720 menu
->hSysMenuOwner
= wndPtr
->hSysMenu
;
3722 WIN_ReleaseWndPtr(wndPtr
);
3724 return bRevert
? 0 : retvalue
;
3728 /*******************************************************************
3729 * SetSystemMenu (USER32.@)
3731 BOOL WINAPI
SetSystemMenu( HWND hwnd
, HMENU hMenu
)
3733 WND
*wndPtr
= WIN_FindWndPtr(hwnd
);
3737 if (wndPtr
->hSysMenu
) DestroyMenu( wndPtr
->hSysMenu
);
3738 wndPtr
->hSysMenu
= MENU_GetSysMenu( hwnd
, hMenu
);
3739 WIN_ReleaseWndPtr(wndPtr
);
3746 /**********************************************************************
3747 * GetMenu (USER32.@)
3749 HMENU WINAPI
GetMenu( HWND hWnd
)
3751 HMENU retvalue
= (HMENU
)GetWindowLongA( hWnd
, GWL_ID
);
3752 TRACE("for %p returning %p\n", hWnd
, retvalue
);
3757 /**********************************************************************
3758 * SetMenu (USER32.@)
3760 BOOL WINAPI
SetMenu( HWND hWnd
, HMENU hMenu
)
3762 TRACE("(%p, %p);\n", hWnd
, hMenu
);
3764 if (hMenu
&& !IsMenu(hMenu
))
3766 WARN("hMenu %p is not a menu handle\n", hMenu
);
3769 if (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3771 hWnd
= WIN_GetFullHandle( hWnd
);
3772 if (GetCapture() == hWnd
) MENU_SetCapture(0); /* release the capture */
3778 if (!(lpmenu
= MENU_GetMenu(hMenu
))) return FALSE
;
3780 lpmenu
->hWnd
= hWnd
;
3781 lpmenu
->Height
= 0; /* Make sure we recalculate the size */
3783 SetWindowLongA( hWnd
, GWL_ID
, (LONG_PTR
)hMenu
);
3785 if (IsWindowVisible(hWnd
))
3786 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3787 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3793 /**********************************************************************
3794 * GetSubMenu (USER32.@)
3796 HMENU WINAPI
GetSubMenu( HMENU hMenu
, INT nPos
)
3800 if (!(lpmi
= MENU_FindItem(&hMenu
,&nPos
,MF_BYPOSITION
))) return 0;
3801 if (!(lpmi
->fType
& MF_POPUP
)) return 0;
3802 return lpmi
->hSubMenu
;
3806 /**********************************************************************
3807 * DrawMenuBar (USER32.@)
3809 BOOL WINAPI
DrawMenuBar( HWND hWnd
)
3812 HMENU hMenu
= GetMenu(hWnd
);
3814 if (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) return FALSE
;
3815 if (!hMenu
|| !(lppop
= MENU_GetMenu( hMenu
))) return FALSE
;
3817 lppop
->Height
= 0; /* Make sure we call MENU_MenuBarCalcSize */
3818 lppop
->hwndOwner
= hWnd
;
3819 SetWindowPos( hWnd
, 0, 0, 0, 0, 0, SWP_NOSIZE
| SWP_NOMOVE
|
3820 SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
3824 /***********************************************************************
3825 * DrawMenuBarTemp (USER32.@)
3829 * called by W98SE desk.cpl Control Panel Applet
3831 * Not 100% sure about the param names, but close.
3833 DWORD WINAPI
DrawMenuBarTemp(HWND hwnd
, HDC hDC
, LPRECT lprect
, HMENU hMenu
, HFONT hFont
)
3840 hMenu
= GetMenu(hwnd
);
3845 lppop
= MENU_GetMenu( hMenu
);
3846 if (lppop
== NULL
|| lprect
== NULL
)
3848 retvalue
= GetSystemMetrics(SM_CYMENU
);
3852 TRACE("(%p, %p, %p, %p, %p)\n", hwnd
, hDC
, lprect
, hMenu
, hFont
);
3854 hfontOld
= SelectObject( hDC
, hFont
);
3856 if (lppop
->Height
== 0)
3857 MENU_MenuBarCalcSize(hDC
, lprect
, lppop
, hwnd
);
3859 lprect
->bottom
= lprect
->top
+ lppop
->Height
;
3861 FillRect(hDC
, lprect
, GetSysColorBrush(COLOR_MENU
) );
3863 if (TWEAK_WineLook
== WIN31_LOOK
)
3865 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_WINDOWFRAME
) );
3866 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
3867 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
3871 SelectObject( hDC
, SYSCOLOR_GetPen(COLOR_3DFACE
));
3872 MoveToEx( hDC
, lprect
->left
, lprect
->bottom
, NULL
);
3873 LineTo( hDC
, lprect
->right
, lprect
->bottom
);
3876 if (lppop
->nItems
== 0)
3878 retvalue
= GetSystemMetrics(SM_CYMENU
);
3882 for (i
= 0; i
< lppop
->nItems
; i
++)
3884 MENU_DrawMenuItem( hwnd
, hMenu
, hwnd
,
3885 hDC
, &lppop
->items
[i
], lppop
->Height
, TRUE
, ODA_DRAWENTIRE
);
3887 retvalue
= lppop
->Height
;
3890 if (hfontOld
) SelectObject (hDC
, hfontOld
);
3894 /***********************************************************************
3895 * EndMenu (USER.187)
3896 * EndMenu (USER32.@)
3898 void WINAPI
EndMenu(void)
3900 /* if we are in the menu code, and it is active */
3901 if (!fEndMenu
&& top_popup
)
3903 /* terminate the menu handling code */
3906 /* needs to be posted to wakeup the internal menu handler */
3907 /* which will now terminate the menu, in the event that */
3908 /* the main window was minimized, or lost focus, so we */
3909 /* don't end up with an orphaned menu */
3910 PostMessageA( top_popup
, WM_CANCELMODE
, 0, 0);
3915 /***********************************************************************
3916 * LookupMenuHandle (USER.217)
3918 HMENU16 WINAPI
LookupMenuHandle16( HMENU16 hmenu
, INT16 id
)
3920 HMENU hmenu32
= HMENU_32(hmenu
);
3922 if (!MENU_FindItem( &hmenu32
, &id32
, MF_BYCOMMAND
)) return 0;
3923 else return HMENU_16(hmenu32
);
3927 /**********************************************************************
3928 * LoadMenu (USER.150)
3930 HMENU16 WINAPI
LoadMenu16( HINSTANCE16 instance
, LPCSTR name
)
3936 if (HIWORD(name
) && name
[0] == '#') name
= (LPCSTR
)atoi( name
+ 1 );
3937 if (!name
) return 0;
3939 instance
= GetExePtr( instance
);
3940 if (!(hRsrc
= FindResource16( instance
, name
, (LPSTR
)RT_MENU
))) return 0;
3941 if (!(handle
= LoadResource16( instance
, hRsrc
))) return 0;
3942 hMenu
= LoadMenuIndirect16(LockResource16(handle
));
3943 FreeResource16( handle
);
3948 /*****************************************************************
3949 * LoadMenuA (USER32.@)
3951 HMENU WINAPI
LoadMenuA( HINSTANCE instance
, LPCSTR name
)
3953 HRSRC hrsrc
= FindResourceA( instance
, name
, (LPSTR
)RT_MENU
);
3954 if (!hrsrc
) return 0;
3955 return LoadMenuIndirectA( (LPCVOID
)LoadResource( instance
, hrsrc
));
3959 /*****************************************************************
3960 * LoadMenuW (USER32.@)
3962 HMENU WINAPI
LoadMenuW( HINSTANCE instance
, LPCWSTR name
)
3964 HRSRC hrsrc
= FindResourceW( instance
, name
, (LPWSTR
)RT_MENU
);
3965 if (!hrsrc
) return 0;
3966 return LoadMenuIndirectW( (LPCVOID
)LoadResource( instance
, hrsrc
));
3970 /**********************************************************************
3971 * LoadMenuIndirect (USER.220)
3973 HMENU16 WINAPI
LoadMenuIndirect16( LPCVOID
template )
3976 WORD version
, offset
;
3977 LPCSTR p
= (LPCSTR
)template;
3979 TRACE("(%p)\n", template );
3980 version
= GET_WORD(p
);
3984 WARN("version must be 0 for Win16\n" );
3987 offset
= GET_WORD(p
);
3988 p
+= sizeof(WORD
) + offset
;
3989 if (!(hMenu
= CreateMenu())) return 0;
3990 if (!MENU_ParseResource( p
, hMenu
, FALSE
))
3992 DestroyMenu( hMenu
);
3995 return HMENU_16(hMenu
);
3999 /**********************************************************************
4000 * LoadMenuIndirectW (USER32.@)
4002 HMENU WINAPI
LoadMenuIndirectW( LPCVOID
template )
4005 WORD version
, offset
;
4006 LPCSTR p
= (LPCSTR
)template;
4008 version
= GET_WORD(p
);
4010 TRACE("%p, ver %d\n", template, version
);
4013 case 0: /* standard format is version of 0 */
4014 offset
= GET_WORD(p
);
4015 p
+= sizeof(WORD
) + offset
;
4016 if (!(hMenu
= CreateMenu())) return 0;
4017 if (!MENU_ParseResource( p
, hMenu
, TRUE
))
4019 DestroyMenu( hMenu
);
4023 case 1: /* extended format is version of 1 */
4024 offset
= GET_WORD(p
);
4025 p
+= sizeof(WORD
) + offset
;
4026 if (!(hMenu
= CreateMenu())) return 0;
4027 if (!MENUEX_ParseResource( p
, hMenu
))
4029 DestroyMenu( hMenu
);
4034 ERR("version %d not supported.\n", version
);
4040 /**********************************************************************
4041 * LoadMenuIndirectA (USER32.@)
4043 HMENU WINAPI
LoadMenuIndirectA( LPCVOID
template )
4045 return LoadMenuIndirectW( template );
4049 /**********************************************************************
4052 BOOL WINAPI
IsMenu(HMENU hmenu
)
4054 LPPOPUPMENU menu
= MENU_GetMenu(hmenu
);
4055 return menu
!= NULL
;
4058 /**********************************************************************
4059 * GetMenuItemInfo_common
4062 static BOOL
GetMenuItemInfo_common ( HMENU hmenu
, UINT item
, BOOL bypos
,
4063 LPMENUITEMINFOW lpmii
, BOOL unicode
)
4065 MENUITEM
*menu
= MENU_FindItem (&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0);
4067 debug_print_menuitem("GetMenuItemInfo_common: ", menu
, "");
4072 if (lpmii
->fMask
& MIIM_TYPE
) {
4073 lpmii
->fType
= menu
->fType
;
4074 switch (MENU_ITEM_TYPE(menu
->fType
)) {
4076 break; /* will be done below */
4079 lpmii
->dwTypeData
= menu
->text
;
4086 /* copy the text string */
4087 if ((lpmii
->fMask
& (MIIM_TYPE
|MIIM_STRING
)) &&
4088 (MENU_ITEM_TYPE(menu
->fType
) == MF_STRING
) && menu
->text
)
4093 len
= strlenW(menu
->text
);
4094 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4095 lstrcpynW(lpmii
->dwTypeData
, menu
->text
, lpmii
->cch
);
4099 len
= WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1, NULL
, 0, NULL
, NULL
);
4100 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4101 if (!WideCharToMultiByte( CP_ACP
, 0, menu
->text
, -1,
4102 (LPSTR
)lpmii
->dwTypeData
, lpmii
->cch
, NULL
, NULL
))
4103 ((LPSTR
)lpmii
->dwTypeData
)[lpmii
->cch
-1] = 0;
4105 /* if we've copied a substring we return its length */
4106 if(lpmii
->dwTypeData
&& lpmii
->cch
)
4108 if (lpmii
->cch
<= len
) lpmii
->cch
--;
4110 else /* return length of string */
4114 if (lpmii
->fMask
& MIIM_FTYPE
)
4115 lpmii
->fType
= menu
->fType
;
4117 if (lpmii
->fMask
& MIIM_BITMAP
)
4118 lpmii
->hbmpItem
= menu
->hbmpItem
;
4120 if (lpmii
->fMask
& MIIM_STATE
)
4121 lpmii
->fState
= menu
->fState
;
4123 if (lpmii
->fMask
& MIIM_ID
)
4124 lpmii
->wID
= menu
->wID
;
4126 if (lpmii
->fMask
& MIIM_SUBMENU
)
4127 lpmii
->hSubMenu
= menu
->hSubMenu
;
4129 if (lpmii
->fMask
& MIIM_CHECKMARKS
) {
4130 lpmii
->hbmpChecked
= menu
->hCheckBit
;
4131 lpmii
->hbmpUnchecked
= menu
->hUnCheckBit
;
4133 if (lpmii
->fMask
& MIIM_DATA
)
4134 lpmii
->dwItemData
= menu
->dwItemData
;
4139 /**********************************************************************
4140 * GetMenuItemInfoA (USER32.@)
4142 BOOL WINAPI
GetMenuItemInfoA( HMENU hmenu
, UINT item
, BOOL bypos
,
4143 LPMENUITEMINFOA lpmii
)
4145 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4146 (LPMENUITEMINFOW
)lpmii
, FALSE
);
4149 /**********************************************************************
4150 * GetMenuItemInfoW (USER32.@)
4152 BOOL WINAPI
GetMenuItemInfoW( HMENU hmenu
, UINT item
, BOOL bypos
,
4153 LPMENUITEMINFOW lpmii
)
4155 return GetMenuItemInfo_common (hmenu
, item
, bypos
,
4160 /* set a menu item text from a ASCII or Unicode string */
4161 inline static void set_menu_item_text( MENUITEM
*menu
, LPCWSTR text
, BOOL unicode
)
4166 menu
->fType
|= MF_SEPARATOR
;
4170 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, (strlenW(text
)+1) * sizeof(WCHAR
) )))
4171 strcpyW( menu
->text
, text
);
4175 LPCSTR str
= (LPCSTR
)text
;
4176 int len
= MultiByteToWideChar( CP_ACP
, 0, str
, -1, NULL
, 0 );
4177 if ((menu
->text
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) )))
4178 MultiByteToWideChar( CP_ACP
, 0, str
, -1, menu
->text
, len
);
4183 /**********************************************************************
4184 * SetMenuItemInfo_common
4187 static BOOL
SetMenuItemInfo_common(MENUITEM
* menu
,
4188 const MENUITEMINFOW
*lpmii
,
4191 if (!menu
) return FALSE
;
4193 debug_print_menuitem("MENU_SetItemInfo_common from: ", menu
, "");
4195 if (lpmii
->fMask
& MIIM_TYPE
) {
4196 /* Get rid of old string. */
4197 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4198 HeapFree(GetProcessHeap(), 0, menu
->text
);
4202 /* make only MENU_ITEM_TYPE bits in menu->fType equal lpmii->fType */
4203 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4204 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4206 menu
->text
= lpmii
->dwTypeData
;
4208 if (IS_STRING_ITEM(menu
->fType
))
4209 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4212 if (lpmii
->fMask
& MIIM_FTYPE
) {
4213 /* free the string when the type is changing */
4214 if ( (!IS_STRING_ITEM(lpmii
->fType
)) && IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4215 HeapFree(GetProcessHeap(), 0, menu
->text
);
4218 menu
->fType
&= ~MENU_ITEM_TYPE(menu
->fType
);
4219 menu
->fType
|= MENU_ITEM_TYPE(lpmii
->fType
);
4220 if ( IS_STRING_ITEM(menu
->fType
) && !menu
->text
)
4221 menu
->fType
|= MF_SEPARATOR
;
4224 if (lpmii
->fMask
& MIIM_STRING
) {
4225 /* free the string when used */
4226 if (IS_STRING_ITEM(menu
->fType
) && menu
->text
) {
4227 HeapFree(GetProcessHeap(), 0, menu
->text
);
4228 set_menu_item_text( menu
, lpmii
->dwTypeData
, unicode
);
4232 if (lpmii
->fMask
& MIIM_STATE
)
4234 /* FIXME: MFS_DEFAULT do we have to reset the other menu items? */
4235 menu
->fState
= lpmii
->fState
;
4238 if (lpmii
->fMask
& MIIM_ID
)
4239 menu
->wID
= lpmii
->wID
;
4241 if (lpmii
->fMask
& MIIM_SUBMENU
) {
4242 menu
->hSubMenu
= lpmii
->hSubMenu
;
4243 if (menu
->hSubMenu
) {
4244 POPUPMENU
*subMenu
= MENU_GetMenu(menu
->hSubMenu
);
4246 subMenu
->wFlags
|= MF_POPUP
;
4247 menu
->fType
|= MF_POPUP
;
4250 /* FIXME: Return an error ? */
4251 menu
->fType
&= ~MF_POPUP
;
4254 menu
->fType
&= ~MF_POPUP
;
4257 if (lpmii
->fMask
& MIIM_CHECKMARKS
)
4259 if (lpmii
->fType
& MFT_RADIOCHECK
)
4260 menu
->fType
|= MFT_RADIOCHECK
;
4262 menu
->hCheckBit
= lpmii
->hbmpChecked
;
4263 menu
->hUnCheckBit
= lpmii
->hbmpUnchecked
;
4265 if (lpmii
->fMask
& MIIM_DATA
)
4266 menu
->dwItemData
= lpmii
->dwItemData
;
4268 debug_print_menuitem("SetMenuItemInfo_common to : ", menu
, "");
4272 /**********************************************************************
4273 * SetMenuItemInfoA (USER32.@)
4275 BOOL WINAPI
SetMenuItemInfoA(HMENU hmenu
, UINT item
, BOOL bypos
,
4276 const MENUITEMINFOA
*lpmii
)
4278 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4279 (const MENUITEMINFOW
*)lpmii
, FALSE
);
4282 /**********************************************************************
4283 * SetMenuItemInfoW (USER32.@)
4285 BOOL WINAPI
SetMenuItemInfoW(HMENU hmenu
, UINT item
, BOOL bypos
,
4286 const MENUITEMINFOW
*lpmii
)
4288 return SetMenuItemInfo_common(MENU_FindItem(&hmenu
, &item
, bypos
? MF_BYPOSITION
: 0),
4292 /**********************************************************************
4293 * SetMenuDefaultItem (USER32.@)
4296 BOOL WINAPI
SetMenuDefaultItem(HMENU hmenu
, UINT uItem
, UINT bypos
)
4302 TRACE("(%p,%d,%d)\n", hmenu
, uItem
, bypos
);
4304 if (!(menu
= MENU_GetMenu(hmenu
))) return FALSE
;
4306 /* reset all default-item flags */
4308 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4310 item
->fState
&= ~MFS_DEFAULT
;
4313 /* no default item */
4322 if ( uItem
>= menu
->nItems
) return FALSE
;
4323 item
[uItem
].fState
|= MFS_DEFAULT
;
4328 for (i
= 0; i
< menu
->nItems
; i
++, item
++)
4330 if (item
->wID
== uItem
)
4332 item
->fState
|= MFS_DEFAULT
;
4341 /**********************************************************************
4342 * GetMenuDefaultItem (USER32.@)
4344 UINT WINAPI
GetMenuDefaultItem(HMENU hmenu
, UINT bypos
, UINT flags
)
4350 TRACE("(%p,%d,%d)\n", hmenu
, bypos
, flags
);
4352 if (!(menu
= MENU_GetMenu(hmenu
))) return -1;
4354 /* find default item */
4358 if (! item
) return -1;
4360 while ( !( item
->fState
& MFS_DEFAULT
) )
4363 if (i
>= menu
->nItems
) return -1;
4366 /* default: don't return disabled items */
4367 if ( (!(GMDI_USEDISABLED
& flags
)) && (item
->fState
& MFS_DISABLED
)) return -1;
4369 /* search rekursiv when needed */
4370 if ( (item
->fType
& MF_POPUP
) && (flags
& GMDI_GOINTOPOPUPS
) )
4373 ret
= GetMenuDefaultItem( item
->hSubMenu
, bypos
, flags
);
4374 if ( -1 != ret
) return ret
;
4376 /* when item not found in submenu, return the popup item */
4378 return ( bypos
) ? i
: item
->wID
;
4383 /**********************************************************************
4384 * InsertMenuItemA (USER32.@)
4386 BOOL WINAPI
InsertMenuItemA(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4387 const MENUITEMINFOA
*lpmii
)
4389 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4390 return SetMenuItemInfo_common(item
, (const MENUITEMINFOW
*)lpmii
, FALSE
);
4394 /**********************************************************************
4395 * InsertMenuItemW (USER32.@)
4397 BOOL WINAPI
InsertMenuItemW(HMENU hMenu
, UINT uItem
, BOOL bypos
,
4398 const MENUITEMINFOW
*lpmii
)
4400 MENUITEM
*item
= MENU_InsertItem(hMenu
, uItem
, bypos
? MF_BYPOSITION
: 0 );
4401 return SetMenuItemInfo_common(item
, lpmii
, TRUE
);
4404 /**********************************************************************
4405 * CheckMenuRadioItem (USER32.@)
4408 BOOL WINAPI
CheckMenuRadioItem(HMENU hMenu
,
4409 UINT first
, UINT last
, UINT check
,
4412 MENUITEM
*mifirst
, *milast
, *micheck
;
4413 HMENU mfirst
= hMenu
, mlast
= hMenu
, mcheck
= hMenu
;
4415 TRACE("%p: %d-%d, check %d, bypos=%d\n", hMenu
, first
, last
, check
, bypos
);
4417 mifirst
= MENU_FindItem (&mfirst
, &first
, bypos
);
4418 milast
= MENU_FindItem (&mlast
, &last
, bypos
);
4419 micheck
= MENU_FindItem (&mcheck
, &check
, bypos
);
4421 if (mifirst
== NULL
|| milast
== NULL
|| micheck
== NULL
||
4422 mifirst
> milast
|| mfirst
!= mlast
|| mfirst
!= mcheck
||
4423 micheck
> milast
|| micheck
< mifirst
)
4426 while (mifirst
<= milast
)
4428 if (mifirst
== micheck
)
4430 mifirst
->fType
|= MFT_RADIOCHECK
;
4431 mifirst
->fState
|= MFS_CHECKED
;
4433 mifirst
->fType
&= ~MFT_RADIOCHECK
;
4434 mifirst
->fState
&= ~MFS_CHECKED
;
4443 /**********************************************************************
4444 * GetMenuItemRect (USER32.@)
4446 * ATTENTION: Here, the returned values in rect are the screen
4447 * coordinates of the item just like if the menu was
4448 * always on the upper left side of the application.
4451 BOOL WINAPI
GetMenuItemRect (HWND hwnd
, HMENU hMenu
, UINT uItem
,
4454 POPUPMENU
*itemMenu
;
4458 TRACE("(%p,%p,%d,%p)\n", hwnd
, hMenu
, uItem
, rect
);
4460 item
= MENU_FindItem (&hMenu
, &uItem
, MF_BYPOSITION
);
4461 referenceHwnd
= hwnd
;
4465 itemMenu
= MENU_GetMenu(hMenu
);
4466 if (itemMenu
== NULL
)
4469 if(itemMenu
->hWnd
== 0)
4471 referenceHwnd
= itemMenu
->hWnd
;
4474 if ((rect
== NULL
) || (item
== NULL
))
4479 MapWindowPoints(referenceHwnd
, 0, (LPPOINT
)rect
, 2);
4485 /**********************************************************************
4486 * SetMenuInfo (USER32.@)
4489 * MIM_APPLYTOSUBMENUS
4490 * actually use the items to draw the menu
4492 BOOL WINAPI
SetMenuInfo (HMENU hMenu
, LPCMENUINFO lpmi
)
4496 TRACE("(%p %p)\n", hMenu
, lpmi
);
4498 if (lpmi
&& (lpmi
->cbSize
==sizeof(MENUINFO
)) && (menu
= MENU_GetMenu(hMenu
)))
4501 if (lpmi
->fMask
& MIM_BACKGROUND
)
4502 menu
->hbrBack
= lpmi
->hbrBack
;
4504 if (lpmi
->fMask
& MIM_HELPID
)
4505 menu
->dwContextHelpID
= lpmi
->dwContextHelpID
;
4507 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4508 menu
->cyMax
= lpmi
->cyMax
;
4510 if (lpmi
->fMask
& MIM_MENUDATA
)
4511 menu
->dwMenuData
= lpmi
->dwMenuData
;
4513 if (lpmi
->fMask
& MIM_STYLE
)
4514 menu
->dwStyle
= lpmi
->dwStyle
;
4521 /**********************************************************************
4522 * GetMenuInfo (USER32.@)
4528 BOOL WINAPI
GetMenuInfo (HMENU hMenu
, LPMENUINFO lpmi
)
4531 TRACE("(%p %p)\n", hMenu
, lpmi
);
4533 if (lpmi
&& (menu
= MENU_GetMenu(hMenu
)))
4536 if (lpmi
->fMask
& MIM_BACKGROUND
)
4537 lpmi
->hbrBack
= menu
->hbrBack
;
4539 if (lpmi
->fMask
& MIM_HELPID
)
4540 lpmi
->dwContextHelpID
= menu
->dwContextHelpID
;
4542 if (lpmi
->fMask
& MIM_MAXHEIGHT
)
4543 lpmi
->cyMax
= menu
->cyMax
;
4545 if (lpmi
->fMask
& MIM_MENUDATA
)
4546 lpmi
->dwMenuData
= menu
->dwMenuData
;
4548 if (lpmi
->fMask
& MIM_STYLE
)
4549 lpmi
->dwStyle
= menu
->dwStyle
;
4557 /**********************************************************************
4558 * SetMenuContextHelpId (USER32.@)
4560 BOOL WINAPI
SetMenuContextHelpId( HMENU hMenu
, DWORD dwContextHelpID
)
4564 TRACE("(%p 0x%08lx)\n", hMenu
, dwContextHelpID
);
4566 if ((menu
= MENU_GetMenu(hMenu
)))
4568 menu
->dwContextHelpID
= dwContextHelpID
;
4575 /**********************************************************************
4576 * GetMenuContextHelpId (USER32.@)
4578 DWORD WINAPI
GetMenuContextHelpId( HMENU hMenu
)
4582 TRACE("(%p)\n", hMenu
);
4584 if ((menu
= MENU_GetMenu(hMenu
)))
4586 return menu
->dwContextHelpID
;
4591 /**********************************************************************
4592 * MenuItemFromPoint (USER32.@)
4594 UINT WINAPI
MenuItemFromPoint(HWND hWnd
, HMENU hMenu
, POINT ptScreen
)
4596 POPUPMENU
*menu
= MENU_GetMenu(hMenu
);
4600 /*FIXME: Do we have to handle hWnd here? */
4601 item
= MENU_FindItemByCoords(menu
, ptScreen
, &pos
);
4607 /**********************************************************************
4608 * translate_accelerator
4610 static BOOL
translate_accelerator( HWND hWnd
, UINT message
, WPARAM wParam
, LPARAM lParam
,
4611 BYTE fVirt
, WORD key
, WORD cmd
)
4615 if (wParam
!= key
) return FALSE
;
4617 if (message
== WM_CHAR
)
4619 if ( !(fVirt
& FALT
) && !(fVirt
& FVIRTKEY
) )
4621 TRACE_(accel
)("found accel for WM_CHAR: ('%c')\n", wParam
& 0xff);
4627 if(fVirt
& FVIRTKEY
)
4630 TRACE_(accel
)("found accel for virt_key %04x (scan %04x)\n",
4631 wParam
, 0xff & HIWORD(lParam
));
4632 if(GetKeyState(VK_SHIFT
) & 0x8000) mask
|= FSHIFT
;
4633 if(GetKeyState(VK_CONTROL
) & 0x8000) mask
|= FCONTROL
;
4634 if(GetKeyState(VK_MENU
) & 0x8000) mask
|= FALT
;
4635 if(mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
4636 TRACE_(accel
)(", but incorrect SHIFT/CTRL/ALT-state\n");
4640 if (!(lParam
& 0x01000000)) /* no special_key */
4642 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
4643 { /* ^^ ALT pressed */
4644 TRACE_(accel
)("found accel for Alt-%c\n", wParam
& 0xff);
4653 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
4655 else if (GetCapture())
4657 else if (!IsWindowEnabled(hWnd
))
4661 HMENU hMenu
, hSubMenu
, hSysMenu
;
4662 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
4664 hMenu
= (GetWindowLongA( hWnd
, GWL_STYLE
) & WS_CHILD
) ? 0 : GetMenu(hWnd
);
4665 hSysMenu
= get_win_sys_menu( hWnd
);
4667 /* find menu item and ask application to initialize it */
4668 /* 1. in the system menu */
4669 hSubMenu
= hSysMenu
;
4671 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4673 SendMessageA(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
4674 if(hSubMenu
!= hSysMenu
)
4676 nPos
= MENU_FindSubMenu(&hSysMenu
, hSubMenu
);
4677 TRACE_(accel
)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu
, hSubMenu
, nPos
);
4678 SendMessageA(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, TRUE
));
4680 uSysStat
= GetMenuState(GetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
4682 else /* 2. in the window's menu */
4686 if(MENU_FindItem(&hSubMenu
, &nPos
, MF_BYCOMMAND
))
4688 SendMessageA(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
4689 if(hSubMenu
!= hMenu
)
4691 nPos
= MENU_FindSubMenu(&hMenu
, hSubMenu
);
4692 TRACE_(accel
)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu
, hSubMenu
, nPos
);
4693 SendMessageA(hWnd
, WM_INITMENUPOPUP
, (WPARAM
)hSubMenu
, MAKELPARAM(nPos
, FALSE
));
4695 uStat
= GetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
4699 if (uSysStat
!= (UINT
)-1)
4701 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
4708 if (uStat
!= (UINT
)-1)
4714 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
4725 if( mesg
==WM_COMMAND
)
4727 TRACE_(accel
)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
4728 SendMessageA(hWnd
, mesg
, 0x10000 | cmd
, 0L);
4730 else if( mesg
==WM_SYSCOMMAND
)
4732 TRACE_(accel
)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
4733 SendMessageA(hWnd
, mesg
, cmd
, 0x00010000L
);
4737 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
4738 * #0: unknown (please report!)
4739 * #1: for WM_KEYUP,WM_SYSKEYUP
4740 * #2: mouse is captured
4741 * #3: window is disabled
4742 * #4: it's a disabled system menu option
4743 * #5: it's a menu option, but window is iconic
4744 * #6: it's a menu option, but disabled
4746 TRACE_(accel
)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg
);
4748 ERR_(accel
)(" unknown reason - please report!");
4753 /**********************************************************************
4754 * TranslateAccelerator (USER32.@)
4755 * TranslateAcceleratorA (USER32.@)
4756 * TranslateAcceleratorW (USER32.@)
4758 INT WINAPI
TranslateAccelerator( HWND hWnd
, HACCEL hAccel
, LPMSG msg
)
4761 LPACCEL16 lpAccelTbl
;
4766 WARN_(accel
)("msg null; should hang here to be win compatible\n");
4769 if (!hAccel
|| !(lpAccelTbl
= (LPACCEL16
) LockResource16(HACCEL_16(hAccel
))))
4771 WARN_(accel
)("invalid accel handle=%p\n", hAccel
);
4774 if ((msg
->message
!= WM_KEYDOWN
&&
4775 msg
->message
!= WM_KEYUP
&&
4776 msg
->message
!= WM_SYSKEYDOWN
&&
4777 msg
->message
!= WM_SYSKEYUP
&&
4778 msg
->message
!= WM_CHAR
)) return 0;
4780 TRACE_(accel
)("TranslateAccelerators hAccel=%p, hWnd=%p,"
4781 "msg->hwnd=%p, msg->message=%04x, wParam=%08x, lParam=%lx\n",
4782 hAccel
,hWnd
,msg
->hwnd
,msg
->message
,msg
->wParam
,msg
->lParam
);
4787 if (translate_accelerator( hWnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
4788 lpAccelTbl
[i
].fVirt
, lpAccelTbl
[i
].key
, lpAccelTbl
[i
].cmd
))
4790 } while ((lpAccelTbl
[i
++].fVirt
& 0x80) == 0);
4791 WARN_(accel
)("couldn't translate accelerator key\n");