From 6fc29ccca7143e50ecff2305f04fe9ed2d0b57a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9=20van=20Geldorp?= Date: Sun, 15 Feb 2004 07:39:12 +0000 Subject: [PATCH] Implement menu handling svn path=/trunk/; revision=8195 --- reactos/include/win32k/kapi.h | 1 + reactos/include/win32k/menu.h | 249 +++ reactos/include/win32k/ntuser.h | 212 +-- reactos/lib/user32/windows/menu.c | 2335 ++++++++++++++++++++++++- reactos/subsys/win32k/include/menu.h | 23 +- reactos/subsys/win32k/ntuser/menu.c | 513 ++++-- reactos/subsys/win32k/ntuser/misc.c | 8 +- reactos/subsys/win32k/ntuser/window.c | 147 +- 8 files changed, 2977 insertions(+), 511 deletions(-) create mode 100644 reactos/include/win32k/menu.h diff --git a/reactos/include/win32k/kapi.h b/reactos/include/win32k/kapi.h index 3b99209ba02..5806a614806 100644 --- a/reactos/include/win32k/kapi.h +++ b/reactos/include/win32k/kapi.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/reactos/include/win32k/menu.h b/reactos/include/win32k/menu.h new file mode 100644 index 00000000000..94064a1fc1f --- /dev/null +++ b/reactos/include/win32k/menu.h @@ -0,0 +1,249 @@ +/* $Id: menu.h,v 1.1 2004/02/15 07:39:12 gvg Exp $ */ + +#ifndef WIN32K_MENU_H_INCLUDED +#define WIN32K_MENU_H_INCLUDED + +typedef struct tagROSMENUINFO { + /* ----------- MENUINFO ----------- */ + DWORD cbSize; + DWORD fMask; + DWORD dwStyle; + UINT cyMax; + HBRUSH hbrBack; + DWORD dwContextHelpID; + ULONG_PTR dwMenuData; + /* ----------- Extra ----------- */ + HMENU Self; /* Handle of this menu */ + WORD Flags; /* Menu flags (MF_POPUP, MF_SYSMENU) */ + UINT FocusedItem; /* Currently focused item */ + UINT MenuItemCount; /* Number of items in the menu */ + HWND Wnd; /* Window containing the menu */ + WORD Width; /* Width of the whole menu */ + WORD Height; /* Height of the whole menu */ + HWND WndOwner; /* window receiving the messages for ownerdraw */ + BOOL TimeToHide; /* Request hiding when receiving a second click in the top-level menu item */ +} ROSMENUINFO, *PROSMENUINFO; + +/* (other FocusedItem values give the position of the focused item) */ +#define NO_SELECTED_ITEM 0xffff + +typedef struct tagROSMENUITEMINFO +{ + /* ----------- MENUITEMINFOW ----------- */ + UINT cbSize; + UINT fMask; + UINT fType; + UINT fState; + UINT wID; + HMENU hSubMenu; + HBITMAP hbmpChecked; + HBITMAP hbmpUnchecked; + DWORD dwItemData; + LPWSTR dwTypeData; + UINT cch; + HBITMAP hbmpItem; + /* ----------- Extra ----------- */ + RECT Rect; /* Item area (relative to menu window) */ + UINT XTab; /* X position of text after Tab */ +} ROSMENUITEMINFO, *PROSMENUITEMINFO; + +typedef struct _SETMENUITEMRECT +{ + UINT uItem; + BOOL fByPosition; + RECT rcRect; +} SETMENUITEMRECT, *PSETMENUITEMRECT; + +DWORD +STDCALL +NtUserBuildMenuItemList( + HMENU hMenu, + PVOID Buffer, + ULONG nBufSize, + DWORD Reserved); + +DWORD +STDCALL +NtUserCheckMenuItem( + HMENU hmenu, + UINT uIDCheckItem, + UINT uCheck); + +HMENU +STDCALL +NtUserCreateMenu(BOOL PopupMenu); + +BOOL +STDCALL +NtUserDeleteMenu( + HMENU hMenu, + UINT uPosition, + UINT uFlags); + +BOOL +STDCALL +NtUserDestroyMenu( + HMENU hMenu); + +DWORD +STDCALL +NtUserDrawMenuBarTemp( + HWND hWnd, + HDC hDC, + PRECT hRect, + HMENU hMenu, + HFONT hFont); + +UINT +STDCALL +NtUserEnableMenuItem( + HMENU hMenu, + UINT uIDEnableItem, + UINT uEnable); + +DWORD +STDCALL +NtUserInsertMenuItem( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + LPCMENUITEMINFOW lpmii); + +BOOL +STDCALL +NtUserEndMenu(VOID); + +UINT STDCALL +NtUserGetMenuDefaultItem( + HMENU hMenu, + UINT fByPos, + UINT gmdiFlags); + +BOOL +STDCALL +NtUserGetMenuBarInfo( + HWND hwnd, + LONG idObject, + LONG idItem, + PMENUBARINFO pmbi); + +UINT +STDCALL +NtUserGetMenuIndex( + HMENU hMenu, + UINT wID); + +BOOL +STDCALL +NtUserGetMenuItemRect( + HWND hWnd, + HMENU hMenu, + UINT uItem, + LPRECT lprcItem); + +HMENU +STDCALL +NtUserGetSystemMenu( + HWND hWnd, + BOOL bRevert); + +BOOL +STDCALL +NtUserHiliteMenuItem( + HWND hwnd, + HMENU hmenu, + UINT uItemHilite, + UINT uHilite); + +BOOL +STDCALL +NtUserMenuInfo( + HMENU hmenu, + PROSMENUINFO lpmi, + BOOL fsog +); + +int +STDCALL +NtUserMenuItemFromPoint( + HWND hWnd, + HMENU hMenu, + DWORD X, + DWORD Y); + +BOOL +STDCALL +NtUserMenuItemInfo( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + PROSMENUITEMINFO lpmii, + BOOL fsog +); + +BOOL +STDCALL +NtUserRemoveMenu( + HMENU hMenu, + UINT uPosition, + UINT uFlags); + +BOOL +STDCALL +NtUserSetMenu( + HWND hWnd, + HMENU hMenu, + BOOL bRepaint); + +BOOL +STDCALL +NtUserSetMenuContextHelpId( + HMENU hmenu, + DWORD dwContextHelpId); + +BOOL +STDCALL +NtUserSetMenuDefaultItem( + HMENU hMenu, + UINT uItem, + UINT fByPos); + +BOOL +STDCALL +NtUserSetMenuFlagRtoL( + HMENU hMenu); + +BOOL +STDCALL +NtUserSetSystemMenu( + HWND hWnd, + HMENU hMenu); + +DWORD +STDCALL +NtUserThunkedMenuInfo( + HMENU hMenu, + LPCMENUINFO lpcmi); + +DWORD +STDCALL +NtUserThunkedMenuItemInfo( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + BOOL bInsert, + LPMENUITEMINFOW lpmii, + PUNICODE_STRING lpszCaption); + +BOOL +STDCALL +NtUserTrackPopupMenuEx( + HMENU hmenu, + UINT fuFlags, + int x, + int y, + HWND hwnd, + LPTPMPARAMS lptpm); + +#endif /* WIN32K_MENU_H_INCLUDED */ + diff --git a/reactos/include/win32k/ntuser.h b/reactos/include/win32k/ntuser.h index 82dd775072b..bbf97b8981e 100644 --- a/reactos/include/win32k/ntuser.h +++ b/reactos/include/win32k/ntuser.h @@ -84,14 +84,6 @@ NtUserBuildHwndList( HWND* pWnd, ULONG nBufSize); -DWORD -STDCALL -NtUserBuildMenuItemList( - HMENU hMenu, - PVOID Buffer, - ULONG nBufSize, - DWORD Reserved); - NTSTATUS STDCALL NtUserBuildNameList( HWINSTA hWinSta, @@ -225,13 +217,6 @@ NtUserChangeDisplaySettings( DWORD dwflags, LPVOID lParam); -DWORD -STDCALL -NtUserCheckMenuItem( - HMENU hmenu, - UINT uIDCheckItem, - UINT uCheck); - HWND STDCALL NtUserChildWindowFromPointEx(HWND Parent, LONG x, @@ -311,10 +296,6 @@ NtUserCreateLocalMemHandle( DWORD Unknown2, DWORD Unknown3); -HMENU -STDCALL -NtUserCreateMenu(BOOL PopupMenu); - HWND STDCALL NtUserCreateWindowEx( @@ -378,13 +359,6 @@ NtUserDeferWindowPos(HDWP WinPosInfo, BOOL STDCALL NtUserDefSetText(HWND WindowHandle, PANSI_STRING Text); -BOOL -STDCALL -NtUserDeleteMenu( - HMENU hMenu, - UINT uPosition, - UINT uFlags); - BOOLEAN STDCALL NtUserDestroyAcceleratorTable( @@ -396,11 +370,6 @@ NtUserDestroyCursorIcon( HANDLE Handle, DWORD Unknown); -BOOL -STDCALL -NtUserDestroyMenu( - HMENU hMenu); - BOOLEAN STDCALL NtUserDestroyWindow(HWND Wnd); @@ -467,35 +436,10 @@ NtUserDrawIconEx( DWORD Unknown0, DWORD Unknown1); -DWORD -STDCALL -NtUserDrawMenuBarTemp( - HWND hWnd, - HDC hDC, - PRECT hRect, - HMENU hMenu, - HFONT hFont); - DWORD STDCALL NtUserEmptyClipboard(VOID); -UINT -STDCALL -NtUserEnableMenuItem( - HMENU hMenu, - UINT uIDEnableItem, - UINT uEnable); - -DWORD -STDCALL -NtUserInsertMenuItem( - HMENU hMenu, - UINT uItem, - BOOL fByPosition, - LPCMENUITEMINFOW lpmii); - - BOOL STDCALL NtUserEnableScrollBar( @@ -509,10 +453,6 @@ NtUserEndDeferWindowPosEx( DWORD Unknown0, DWORD Unknown1); -BOOL -STDCALL -NtUserEndMenu(VOID); - BOOL STDCALL NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs); @@ -693,16 +633,17 @@ STDCALL NtUserGetCursorInfo( PCURSORINFO pci); -UINT STDCALL -NtUserGetMenuDefaultItem( - HMENU hMenu, - UINT fByPos, - UINT gmdiFlags); +HDC +STDCALL +NtUserGetDC( + HWND hWnd); -HDC STDCALL -NtUserGetDC(HWND hWnd); - -HDC STDCALL NtUserGetDCEx(HWND hWnd, HANDLE hRegion, ULONG Flags); +HDC +STDCALL +NtUserGetDCEx( + HWND hWnd, + HANDLE hRegion, + ULONG Flags); UINT STDCALL @@ -782,28 +723,6 @@ STDCALL NtUserGetListBoxInfo( DWORD Unknown0); -BOOL -STDCALL -NtUserGetMenuBarInfo( - HWND hwnd, - LONG idObject, - LONG idItem, - PMENUBARINFO pmbi); - -UINT -STDCALL -NtUserGetMenuIndex( - HMENU hMenu, - UINT wID); - -BOOL -STDCALL -NtUserGetMenuItemRect( - HWND hWnd, - HMENU hMenu, - UINT uItem, - LPRECT lprcItem); - BOOL STDCALL NtUserGetMessage( @@ -858,12 +777,6 @@ NtUserGetScrollInfo( int fnBar, LPSCROLLINFO lpsi); -HMENU -STDCALL -NtUserGetSystemMenu( - HWND hWnd, - BOOL bRevert); - HDESK STDCALL NtUserGetThreadDesktop( @@ -913,14 +826,6 @@ STDCALL NtUserHideCaret( HWND hWnd); -BOOL -STDCALL -NtUserHiliteMenuItem( - HWND hwnd, - HMENU hmenu, - UINT uItemHilite, - UINT uHilite); - DWORD STDCALL NtUserImpersonateDdeClientWindow( @@ -1024,32 +929,6 @@ NtUserMapVirtualKeyEx( UINT keyCode, DWORD keyboardId, HKL dwhkl ); -BOOL -STDCALL -NtUserMenuInfo( - HMENU hmenu, - LPMENUINFO lpmi, - BOOL fsog -); - -int -STDCALL -NtUserMenuItemFromPoint( - HWND hWnd, - HMENU hMenu, - DWORD X, - DWORD Y); - -BOOL -STDCALL -NtUserMenuItemInfo( - HMENU hMenu, - UINT uItem, - BOOL fByPosition, - LPMENUITEMINFOW lpmii, - BOOL fsog -); - DWORD STDCALL NtUserMessageCall( @@ -1226,13 +1105,6 @@ NtUserRegisterTasklist( UINT STDCALL NtUserRegisterWindowMessage(PUNICODE_STRING MessageName); -BOOL -STDCALL -NtUserRemoveMenu( - HMENU hMenu, - UINT uPosition, - UINT uFlags); - HANDLE STDCALL NtUserRemoveProp(HWND hWnd, ATOM Atom); @@ -1419,31 +1291,6 @@ STDCALL NtUserSetLogonNotifyWindow( DWORD Unknown0); -BOOL -STDCALL -NtUserSetMenu( - HWND hWnd, - HMENU hMenu, - BOOL bRepaint); - -BOOL -STDCALL -NtUserSetMenuContextHelpId( - HMENU hmenu, - DWORD dwContextHelpId); - -BOOL -STDCALL -NtUserSetMenuDefaultItem( - HMENU hMenu, - UINT uItem, - UINT fByPos); - -BOOL -STDCALL -NtUserSetMenuFlagRtoL( - HMENU hMenu); - BOOL STDCALL NtUserSetObjectInformation( @@ -1504,12 +1351,6 @@ NtUserSetSystemCursor( HCURSOR hcur, DWORD id); -BOOL -STDCALL -NtUserSetSystemMenu( - HWND hWnd, - HMENU hMenu); - BOOL STDCALL NtUserSetThreadDesktop( @@ -1653,22 +1494,6 @@ NtUserSystemParametersInfo( PVOID pvParam, UINT fWinIni); -DWORD -STDCALL -NtUserThunkedMenuInfo( - HMENU hMenu, - LPCMENUINFO lpcmi); - -DWORD -STDCALL -NtUserThunkedMenuItemInfo( - HMENU hMenu, - UINT uItem, - BOOL fByPosition, - BOOL bInsert, - LPMENUITEMINFOW lpmii, - PUNICODE_STRING lpszCaption); - int STDCALL NtUserToUnicodeEx( @@ -1685,16 +1510,6 @@ STDCALL NtUserTrackMouseEvent( DWORD Unknown0); -BOOL -STDCALL -NtUserTrackPopupMenuEx( - HMENU hmenu, - UINT fuFlags, - int x, - int y, - HWND hwnd, - LPTPMPARAMS lptpm); - int STDCALL NtUserTranslateAccelerator( @@ -1876,13 +1691,6 @@ NtUserSetScrollBarInfo( LONG idObject, SETSCROLLBARINFO *info); -typedef struct _SETMENUITEMRECT -{ - UINT uItem; - BOOL fByPosition; - RECT rcRect; -} SETMENUITEMRECT, *PSETMENUITEMRECT; - #endif /* __WIN32K_NTUSER_H */ /* EOF */ diff --git a/reactos/lib/user32/windows/menu.c b/reactos/lib/user32/windows/menu.c index e3152273e98..7b5eecfc316 100644 --- a/reactos/lib/user32/windows/menu.c +++ b/reactos/lib/user32/windows/menu.c @@ -2,6 +2,11 @@ * ReactOS kernel * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team * + * Partly based on Wine code + * Copyright 1993 Martin Ayotte + * Copyright 1994 Alexandre Julliard + * Copyright 1997 Morten Welinder + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -16,7 +21,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: menu.c,v 1.47 2004/02/01 20:50:10 weiden Exp $ +/* $Id: menu.c,v 1.48 2004/02/15 07:39:12 gvg Exp $ * * PROJECT: ReactOS user32.dll * FILE: lib/user32/windows/menu.c @@ -30,7 +35,6 @@ #include #include -#include #include #include #include @@ -40,17 +44,40 @@ #include "user32/regcontrol.h" #include "../controls/controls.h" +#define NDEBUG +#include + +/* internal popup menu window messages */ +#define MM_SETMENUHANDLE (WM_USER + 0) +#define MM_GETMENUHANDLE (WM_USER + 1) + +/* Internal MenuTrackMenu() flags */ +#define TPM_INTERNAL 0xF0000000 +#define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */ +#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */ +#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */ + /* TYPES *********************************************************************/ -#define MENU_TYPE_MASK ((MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)) +#define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR) -#define MENU_ITEM_TYPE(flags) \ - ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)) +#define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK) +#define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags)) +#define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags)) + +#define IS_SYSTEM_MENU(MenuInfo) \ + (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU)) + +#define IS_SYSTEM_POPUP(MenuInfo) \ + (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU)) #define MENU_BAR_ITEMS_SPACE (12) #define SEPARATOR_HEIGHT (5) #define MENU_TAB_SPACE (8) +#define ITEM_PREV -1 +#define ITEM_NEXT 1 + #ifndef MF_END #define MF_END (0x0080) #endif @@ -64,6 +91,21 @@ #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */ #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */ +/* internal flags for menu tracking */ + +#define TF_ENDMENU 0x0001 +#define TF_SUSPENDPOPUP 0x0002 +#define TF_SKIPREMOVE 0x0004 + +typedef struct +{ + UINT TrackFlags; + HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/ + HMENU TopMenu; /* initial menu */ + HWND OwnerWnd; /* where notifications are sent */ + POINT Pt; +} MTRACKER; + static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); /********************************************************************* @@ -98,13 +140,638 @@ const struct builtin_class_descr POPUPMENU_builtin_class = HFONT hMenuFont = NULL; HFONT hMenuFontBold = NULL; -static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +/* Flag set by EndMenu() to force an exit from menu tracking */ +static BOOL fEndMenu = FALSE; + +/* Use global popup window because there's no way 2 menus can + * be tracked at the same time. */ +static HWND TopPopup; + +/* Dimension of the menu bitmaps */ +static WORD ArrowBitmapWidth = 0/*, ArrowBitmapHeight = 0*/; + + +/*********************************************************************** + * MenuGetRosMenuInfo + * + * Get full information about menu + */ +static BOOL FASTCALL +MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu) { - switch(message) - { + MenuInfo->cbSize = sizeof(ROSMENUINFO); + MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE; + + return NtUserMenuInfo(Menu, MenuInfo, FALSE); +} + +/*********************************************************************** + * MenuSetRosMenuInfo + * + * Set full information about menu + */ +static BOOL FASTCALL +MenuSetRosMenuInfo(PROSMENUINFO MenuInfo) +{ + MenuInfo->cbSize = sizeof(ROSMENUINFO); + MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE; + + return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE); +} + +/*********************************************************************** + * MenuInitRosMenuItemInfo + * + * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo + */ +static VOID FASTCALL +MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) +{ + ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO)); + ItemInfo->cbSize = sizeof(ROSMENUITEMINFO); +} + +/*********************************************************************** + * MenuGetRosMenuItemInfo + * + * Get full information about a menu item + */ +#define INITIAL_STRING_SIZE 32 /* in WCHARs */ +static BOOL FASTCALL +MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) +{ + UNICODE_STRING Text; + + if (MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType) && NULL != ItemInfo->dwTypeData) + { + /* There's already a buffer allocated */ + Text.Buffer = ItemInfo->dwTypeData; + Text.Length = ItemInfo->cch * sizeof(WCHAR); + Text.MaximumLength = (ItemInfo->cch < INITIAL_STRING_SIZE ? INITIAL_STRING_SIZE + : ItemInfo->cch + 1) * sizeof(WCHAR); + } + else + { + Text.Buffer = HeapAlloc(GetProcessHeap(), 0, INITIAL_STRING_SIZE * sizeof(WCHAR)); + if (NULL == Text.Buffer) + { + return FALSE; + } + Text.Length = 0; + Text.MaximumLength = INITIAL_STRING_SIZE * sizeof(WCHAR); + ItemInfo->cch = INITIAL_STRING_SIZE - 1; + } + ItemInfo->dwTypeData = (LPWSTR) &Text; + + ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE + | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE; + + if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE)) + { + if (NULL != Text.Buffer) + { + HeapFree(GetProcessHeap(), 0, Text.Buffer); + ItemInfo->dwTypeData = NULL; + ItemInfo->cch = 0; + } + ItemInfo->fType = 0; + } + + if (MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType)) + { + /* We have a string... */ + if (Text.MaximumLength < (ItemInfo->cch + 1) * sizeof(WCHAR)) + { + /* ...but we didn't allocate enough memory. Let's try again */ + HeapFree(GetProcessHeap(), 0, Text.Buffer); + Text.Buffer = HeapAlloc(GetProcessHeap(), 0, (ItemInfo->cch + 1) * sizeof(WCHAR)); + if (NULL == Text.Buffer) + { + ItemInfo->dwTypeData = NULL; + ItemInfo->cch = 0; + return FALSE; + } + Text.Length = (ItemInfo->cch + 1) * sizeof(WCHAR); + Text.MaximumLength = (ItemInfo->cch + 1) * sizeof(WCHAR); + ItemInfo->cch++; + if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE)) + { + HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); + ItemInfo->dwTypeData = NULL; + ItemInfo->cch = 0; + ItemInfo->fType = 0; + } + } + } + + if (MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType)) + { + ItemInfo->dwTypeData = Text.Buffer; + ItemInfo->cch = Text.Length / sizeof(WCHAR); + Text.Buffer[ItemInfo->cch] = L'\0'; + } + else + { + /* Not a string, clean up the buffer */ + if (NULL != Text.Buffer) + { + HeapFree(GetProcessHeap(), 0, Text.Buffer); + } + } + + return TRUE; +} + +/*********************************************************************** + * MenuSetRosMenuItemInfo + * + * Set full information about a menu item + */ +static BOOL FASTCALL +MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) +{ + UNICODE_STRING Text; + BOOL StringVal; + BOOL Ret; + + StringVal = (MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType) && NULL != ItemInfo->dwTypeData); + if (StringVal) + { + Text.Buffer = ItemInfo->dwTypeData; + Text.Length = wcslen(Text.Buffer) * sizeof(WCHAR); + Text.MaximumLength = Text.Length + sizeof(WCHAR); + ItemInfo->dwTypeData = (LPWSTR) &Text; + } + ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE + | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE; + + Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE); + + if (StringVal) + { + ItemInfo->dwTypeData = Text.Buffer; + } + + return Ret; +} + +/*********************************************************************** + * MenuCleanupRosMenuItemInfo + * + * Cleanup after use of MenuGet/SetRosMenuItemInfo + */ +static VOID FASTCALL +MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) +{ + if (MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType) && NULL != ItemInfo->dwTypeData) + { + HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); + ItemInfo->dwTypeData = NULL; + ItemInfo->cch = 0; + } +} + +/*********************************************************************** + * MenuDrawMenuItem + * + * Draw a single menu item. + */ +static void FASTCALL +MenuDrawMenuItem(HWND Wnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC Dc, + PROSMENUITEMINFO Item, UINT Height, BOOL MenuBar, UINT Action) +{ + RECT Rect; + PWCHAR Text; + + if (0 != (Item->fType & MF_SYSMENU)) + { +#if 0 /* FIXME */ + if( !IsIconic(hwnd) ) { + if (TWEAK_WineLook > WIN31_LOOK) + NC_DrawSysButton95( hwnd, hdc, + lpitem->fState & + (MF_HILITE | MF_MOUSESELECT) ); + else + NC_DrawSysButton( hwnd, hdc, + lpitem->fState & + (MF_HILITE | MF_MOUSESELECT) ); + } +#endif + + return; + } + + if (0 != (Item->fType & MF_OWNERDRAW)) + { + /* + ** Experimentation under Windows reveals that an owner-drawn + ** menu is given the rectangle which includes the space it requested + ** in its response to WM_MEASUREITEM _plus_ width for a checkmark + ** and a popup-menu arrow. This is the value of lpitem->rect. + ** Windows will leave all drawing to the application except for + ** the popup-menu arrow. Windows always draws that itself, after + ** the menu owner has finished drawing. + */ + DRAWITEMSTRUCT dis; + + dis.CtlType = ODT_MENU; + dis.CtlID = 0; + dis.itemID = Item->wID; + dis.itemData = (DWORD)Item->dwItemData; + dis.itemState = 0; + if (0 != (Item->fState & MF_CHECKED)) + { + dis.itemState |= ODS_CHECKED; + } + if (0 != (Item->fState & MF_GRAYED)) + { + dis.itemState |= ODS_GRAYED | ODS_DISABLED; + } + if (0 != (Item->fState & MF_HILITE)) + { + dis.itemState |= ODS_SELECTED; + } + dis.itemAction = Action; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */ + dis.hwndItem = (HWND) MenuInfo->Self; + dis.hDC = Dc; + dis.rcItem = Item->Rect; + DPRINT("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, " + "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hwndOwner, + dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem, + dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, + dis.rcItem.bottom); + SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis); + /* Fall through to draw popup-menu arrow */ + } + + DPRINT("rect={%ld,%ld,%ld,%ld}\n", Item->rect.left, Item->rect.top, + Item->rect.right, Item->rect.bottom); + + if (MenuBar && 0 != (Item->fType & MF_SEPARATOR)) + { + return; + } + + Rect = Item->Rect; + + if (0 == (Item->fType & MF_OWNERDRAW)) + { + if (Item->fState & MF_HILITE) + { + if (MenuBar) + { + DrawEdge(Dc, &Rect, BDR_SUNKENOUTER, BF_RECT); + } + else + { + FillRect(Dc, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + } + else + { + FillRect(Dc, &Rect, GetSysColorBrush(COLOR_MENU)); + } + } + + SetBkMode(Dc, TRANSPARENT); + + if (0 == (Item->fType & MF_OWNERDRAW)) + { + /* vertical separator */ + if (! MenuBar && 0 != (Item->fType & MF_MENUBARBREAK)) + { + RECT rc = Rect; + rc.top = 3; + rc.bottom = Height - 3; + DrawEdge(Dc, &rc, EDGE_ETCHED, BF_LEFT); + } + + /* horizontal separator */ + if (0 != (Item->fType & MF_SEPARATOR)) + { + RECT rc = Rect; + rc.left++; + rc.right--; + rc.top += SEPARATOR_HEIGHT / 2; + DrawEdge(Dc, &rc, EDGE_ETCHED, BF_TOP); + + return; + } + } + + /* Setup colors */ + + if (0 != (Item->fState & MF_HILITE)) + { + if (MenuBar) + { + SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT)); + SetBkColor(Dc, GetSysColor(COLOR_MENU)); + } + else + { + if (0 != (Item->fState & MF_GRAYED)) + { + SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT)); + } + else + { + SetTextColor(Dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + } + SetBkColor(Dc, GetSysColor(COLOR_HIGHLIGHT)); + } + } + else + { + if (0 != (Item->fState & MF_GRAYED)) + { + SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT)); + } + else + { + SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT)); + } + SetBkColor(Dc, GetSysColor(COLOR_MENU)); + } + +#if 0 + /* helper lines for debugging */ + FrameRect(Dc, &Rect, GetStockObject(BLACK_BRUSH)); + SelectObject(Dc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME)); + MoveToEx(Dc, Rect.left, (Rect.top + Rect.bottom) / 2, NULL); + LineTo(Dc, Rect.right, (Rect.top + Rect.bottom) / 2); +#endif + + if (! MenuBar) + { + INT y = Rect.top + Rect.bottom; + UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK); + UINT CheckBitmapHeight = GetSystemMetrics(SM_CYMENUCHECK); + + if (0 == (Item->fType & MF_OWNERDRAW)) + { + /* Draw the check mark + * + * FIXME: + * Custom checkmark bitmaps are monochrome but not always 1bpp. + */ +#if 0 /* FIXME */ + HBITMAP bm = 0 != (Item->fState & MF_CHECKED) ? Item->hCheckBit : Item->hUnCheckBit; + if (NULL != bm) /* we have a custom bitmap */ + { + HDC DcMem = CreateCompatibleDC(Dc); + SelectObject(DcMem, bm); + BitBlt(Dc, Rect.left, (y - CheckBitmapHeight) / 2, + CheckBitmapWidth, CheckBitmapHeight, + DcMem, 0, 0, SRCCOPY); + DeleteDC(DcMem); + } + else if (0 != (Item->fState & MF_CHECKED)) /* standard bitmaps */ +#else + if (0 != (Item->fState & MF_CHECKED)) /* standard bitmaps */ +#endif + { + RECT r; + HBITMAP bm = CreateBitmap(CheckBitmapWidth, CheckBitmapHeight, 1, 1, NULL); + HDC DcMem = CreateCompatibleDC(Dc); + SelectObject(DcMem, bm); + SetRect( &r, 0, 0, CheckBitmapWidth, CheckBitmapHeight); + DrawFrameControl(DcMem, &r, DFC_MENU, + 0 != (Item->fType & MFT_RADIOCHECK) ? + DFCS_MENUBULLET : DFCS_MENUCHECK); + BitBlt(Dc, Rect.left, (y - r.bottom) / 2, r.right, r.bottom, + DcMem, 0, 0, SRCCOPY ); + DeleteDC(DcMem); + DeleteObject(bm); + } + } + +#if 0 /* FIXME */ + /* Draw the popup-menu arrow */ + if (lpitem->fType & MF_POPUP) + { + HDC hdcMem = CreateCompatibleDC( hdc ); + HBITMAP hOrigBitmap; + + hOrigBitmap = SelectObject( hdcMem, hStdMnArrow ); + BitBlt( hdc, rect.right - arrow_bitmap_width - 1, + (y - arrow_bitmap_height) / 2, + arrow_bitmap_width, arrow_bitmap_height, + hdcMem, 0, 0, SRCCOPY ); + SelectObject( hdcMem, hOrigBitmap ); + DeleteDC( hdcMem ); + } +#endif + + Rect.left += CheckBitmapWidth; + Rect.right -= ArrowBitmapWidth; + } + + /* Done for owner-drawn */ + if (0 != (Item->fType & MF_OWNERDRAW)) + { + return; + } + + /* Draw the item text or bitmap */ + if (IS_BITMAP_ITEM(Item->fType)) + { +#if 0 /* FIXME */ + MENU_DrawBitmapItem( hdc, lpitem, &rect, menuBar ); +#endif + return; + } + /* No bitmap - process text if present */ + else if (IS_STRING_ITEM(Item->fType)) + { + register int i; + HFONT FontOld = NULL; + + UINT uFormat = MenuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE + : DT_LEFT | DT_VCENTER | DT_SINGLELINE; + + if (0 != (Item->fState & MFS_DEFAULT)) + { + FontOld = SelectObject(Dc, hMenuFontBold); + } + + if (MenuBar) + { + Rect.left += MENU_BAR_ITEMS_SPACE / 2; + Rect.right -= MENU_BAR_ITEMS_SPACE / 2; + } + + Text = (PWCHAR) Item->dwTypeData; + for (i = 0; L'\0' != Text[i]; i++) + { + if (L'\t' == Text[i] || L'\b' == Text[i]) + { + break; + } + } + + if (0 != (Item->fState & MF_GRAYED)) + { + if (0 == (Item->fState & MF_HILITE)) + { + ++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom; + SetTextColor(Dc, RGB(0xff, 0xff, 0xff)); + DrawTextW(Dc, Text, i, &Rect, uFormat); + --Rect.left; --Rect.top; --Rect.right; --Rect.bottom; + } + SetTextColor(Dc, RGB(0x80, 0x80, 0x80)); + } + + DrawTextW(Dc, Text, i, &Rect, uFormat); + + /* paint the shortcut text */ + if (! MenuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */ + { + if (L'\t' == Text[i]) + { + Rect.left = Item->XTab; + uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; + } + else + { + uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE; + } + + if (0 != (Item->fState & MF_GRAYED)) + { + if (0 == (Item->fState & MF_HILITE)) + { + ++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom; + SetTextColor(Dc, RGB(0xff, 0xff, 0xff)); + DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat); + --Rect.left; --Rect.top; --Rect.right; --Rect.bottom; + } + SetTextColor(Dc, RGB(0x80, 0x80, 0x80)); + } + DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat); + } + + if (NULL != FontOld) + { + SelectObject(Dc, FontOld); + } + } +} + +/*********************************************************************** + * MenuDrawPopupMenu + * + * Paint a popup menu. + */ +static void FASTCALL +MenuDrawPopupMenu(HWND Wnd, HDC Dc, HMENU Menu) +{ + HBRUSH PrevBrush = NULL; + HPEN PrevPen; + RECT Rect; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + UINT u; + + DPRINT("wnd=%x dc=%x menu=%x\n", Wnd, Dc, Menu); + + GetClientRect(Wnd, &Rect); + + if (NULL != (PrevBrush = SelectObject(Dc, GetSysColorBrush(COLOR_MENU))) + && NULL != SelectObject(Dc, hMenuFont)) + { + Rectangle(Dc, Rect.left, Rect.top, Rect.right, Rect.bottom); + + PrevPen = SelectObject(Dc, GetStockObject(NULL_PEN)); + if (NULL != PrevPen) + { + DrawEdge(Dc, &Rect, EDGE_RAISED, BF_RECT); + + /* draw menu items */ + + if (MenuGetRosMenuInfo(&MenuInfo, Menu) && 0 != MenuInfo.MenuItemCount) + { + MenuInitRosMenuItemInfo(&ItemInfo); + + for (u = 0; u < MenuInfo.MenuItemCount; u++) + { + if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo)) + { + MenuDrawMenuItem(Wnd, &MenuInfo, MenuInfo.WndOwner, Dc, &ItemInfo, + MenuInfo.Height, FALSE, ODA_DRAWENTIRE); + } + } + } + } + else + { + SelectObject(Dc, PrevBrush); + } + } +} + +static LRESULT WINAPI +PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) +{ + DPRINT("hwnd=%x msg=0x%04x wp=0x%04x lp=0x%08lx\n", Wnd, Message, wParam, lParam); + + switch(Message) + { + case WM_CREATE: + { + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + SetWindowLongW(Wnd, 0, (LONG) cs->lpCreateParams); + return 0; + } + + case WM_MOUSEACTIVATE: /* We don't want to be activated */ + return MA_NOACTIVATE; + + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(Wnd, &ps); + MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongW(Wnd, 0)); + EndPaint(Wnd, &ps); + return 0; + } + + case WM_ERASEBKGND: + return 1; + + case WM_DESTROY: + /* zero out global pointer in case resident popup window was destroyed. */ + if (Wnd == TopPopup) + { + TopPopup = NULL; + } + break; + + case WM_SHOWWINDOW: + if (0 != wParam) + { + if (0 == GetWindowLongW(Wnd, 0)) + { + OutputDebugStringA("no menu to display\n"); + } + } + else + { + SetWindowLongW(Wnd, 0, 0); + } + break; + + case MM_SETMENUHANDLE: + SetWindowLongW(Wnd, 0, wParam); + break; + + case MM_GETMENUHANDLE: + return GetWindowLongW(Wnd, 0); + default: - return DefWindowProcW(hwnd, message, wParam, lParam); - } + return DefWindowProcW(Wnd, Message, wParam, lParam); + } + return 0; } @@ -119,7 +786,7 @@ static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu) { WORD resinfo; - + do { MENUITEMINFOW mii; @@ -346,6 +1013,377 @@ DrawMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWST } +/*********************************************************************** + * MenuCalcItemSize + * + * Calculate the size of the menu item and store it in ItemInfo->rect. + */ +static void FASTCALL +MenuCalcItemSize(HDC Dc, PROSMENUITEMINFO ItemInfo, HWND WndOwner, + INT OrgX, INT OrgY, BOOL MenuBar) +{ + PWCHAR p; + UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK); + + DPRINT("dc=%x owner=%x (%d,%d)\n", Dc, WndOwner, OrgX, OrgY); + + SetRect(&ItemInfo->Rect, OrgX, OrgY, OrgX, OrgY); + + if (0 != (ItemInfo->fType & MF_OWNERDRAW)) + { + /* + ** Experimentation under Windows reveals that an owner-drawn + ** menu is expected to return the size of the content part of + ** the menu item, not including the checkmark nor the submenu + ** arrow. Windows adds those values itself and returns the + ** enlarged rectangle on subsequent WM_DRAWITEM messages. + */ + MEASUREITEMSTRUCT mis; + mis.CtlType = ODT_MENU; + mis.CtlID = 0; + mis.itemID = ItemInfo->wID; + mis.itemData = (DWORD)ItemInfo->dwItemData; + mis.itemHeight = 0; + mis.itemWidth = 0; + SendMessageW(WndOwner, WM_MEASUREITEM, 0, (LPARAM) &mis); + ItemInfo->Rect.right += mis.itemWidth; + + if (MenuBar) + { + ItemInfo->Rect.right += MENU_BAR_ITEMS_SPACE; + + /* under at least win95 you seem to be given a standard + height for the menu and the height value is ignored */ + + ItemInfo->Rect.bottom += GetSystemMetrics(SM_CYMENU) - 1; + } + else + { + ItemInfo->Rect.bottom += mis.itemHeight; + } + + DPRINT("id=%04x size=%dx%d\n", ItemInfo->wID, mis.itemWidth, mis.itemHeight); + /* Fall through to get check/arrow width calculation. */ + } + + if (0 != (ItemInfo->fType & MF_SEPARATOR)) + { + ItemInfo->Rect.bottom += SEPARATOR_HEIGHT; + return; + } + + if (! MenuBar) + { + ItemInfo->Rect.right += 2 * CheckBitmapWidth; + if (0 != (ItemInfo->fType & MF_POPUP)) + { + ItemInfo->Rect.right += ArrowBitmapWidth; + } + } + + if (0 != (ItemInfo->fType & MF_OWNERDRAW)) + { + return; + } + + if (IS_BITMAP_ITEM(ItemInfo->fType)) + { +#if 0 /* FIXME */ + SIZE size; + + MENU_GetBitmapItemSize( (int)lpitem->text, lpitem->dwItemData, &size ); + lpitem->rect.right += size.cx; + lpitem->rect.bottom += size.cy; + + /* Leave space for the sunken border */ + lpitem->rect.right += 2; + lpitem->rect.bottom += 2; +#endif + } + + /* it must be a text item - unless it's the system menu */ + if (0 == (ItemInfo->fType & MF_SYSMENU) && IS_STRING_ITEM(ItemInfo->fType)) + { + SIZE Size; + + GetTextExtentPoint32W(Dc, (LPWSTR) ItemInfo->dwTypeData, + wcslen((LPWSTR) ItemInfo->dwTypeData), &Size); + + ItemInfo->Rect.right += Size.cx; + ItemInfo->Rect.bottom += max(Size.cy, GetSystemMetrics(SM_CYMENU) - 1); + ItemInfo->XTab = 0; + + if (MenuBar) + { + ItemInfo->Rect.right += MENU_BAR_ITEMS_SPACE; + } + else if ((p = wcschr((LPWSTR) ItemInfo->dwTypeData, L'\t' )) != NULL) + { + /* Item contains a tab (only meaningful in popup menus) */ + GetTextExtentPoint32W(Dc, (LPWSTR) ItemInfo->dwTypeData, + (int)(p - (LPWSTR) ItemInfo->dwTypeData), &Size); + ItemInfo->XTab = CheckBitmapWidth + MENU_TAB_SPACE + Size.cx; + ItemInfo->Rect.right += MENU_TAB_SPACE; + } + else + { + if (NULL != wcschr((LPWSTR) ItemInfo->dwTypeData, L'\b')) + { + ItemInfo->Rect.right += MENU_TAB_SPACE; + } + ItemInfo->XTab = ItemInfo->Rect.right - CheckBitmapWidth + - ArrowBitmapWidth; + } + } + + DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo->Rect.left, ItemInfo->Rect.top, ItemInfo->Rect.right, ItemInfo->Rect.bottom); +} + +/*********************************************************************** + * MenuPopupMenuCalcSize + * + * Calculate the size of a popup menu. + */ +static void FASTCALL +MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner) +{ + ROSMENUITEMINFO ItemInfo; + HDC Dc; + int Start, i; + int OrgX, OrgY, MaxX, MaxTab, MaxTabWidth; + + MenuInfo->Width = MenuInfo->Height = 0; + if (0 == MenuInfo->MenuItemCount) + { + MenuSetRosMenuInfo(MenuInfo); + return; + } + + Dc = GetDC(NULL); + SelectObject(Dc, hMenuFont); + + Start = 0; + MaxX = 2 + 1; + + MenuInitRosMenuItemInfo(&ItemInfo); + while (Start < MenuInfo->MenuItemCount) + { + OrgX = MaxX; + OrgY = 2; + + MaxTab = MaxTabWidth = 0; + + /* Parse items until column break or end of menu */ + for (i = Start; i < MenuInfo->MenuItemCount; i++) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuSetRosMenuInfo(MenuInfo); + return; + } + if (i != Start && + 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) + { + break; + } + + MenuCalcItemSize(Dc, &ItemInfo, WndOwner, OrgX, OrgY, FALSE); + if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuSetRosMenuInfo(MenuInfo); + return; + } + + if (0 != (ItemInfo.fType & MF_MENUBARBREAK)) + { + OrgX++; + } + MaxX = max(MaxX, ItemInfo.Rect.right); + OrgY = ItemInfo.Rect.bottom; + if (IS_STRING_ITEM(ItemInfo.fType) && 0 != ItemInfo.XTab) + { + MaxTab = max(MaxTab, ItemInfo.XTab); + MaxTabWidth = max(MaxTabWidth, ItemInfo.Rect.right - ItemInfo.XTab); + } + } + + /* Finish the column (set all items to the largest width found) */ + MaxX = max(MaxX, MaxTab + MaxTabWidth); + while (Start < i) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo)) + { + ItemInfo.Rect.right = MaxX; + if (IS_STRING_ITEM(ItemInfo.fType) && 0 != ItemInfo.XTab) + { + ItemInfo.XTab = MaxTab; + } + MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo); + } + Start++; + } + MenuInfo->Height = max(MenuInfo->Height, OrgY); + } + + MenuInfo->Width = MaxX; + + /* space for 3d border */ + MenuInfo->Height += 2; + MenuInfo->Width += 2; + + ReleaseDC(NULL, Dc); + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuSetRosMenuInfo(MenuInfo); +} + +#if 0 /* Not used yet */ +/*********************************************************************** + * MenuMenuBarCalcSize + * + * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap + * height is off by 1 pixel which causes lengthy window relocations when + * active document window is maximized/restored. + * + * Calculate the size of the menu bar. + */ +static void FASTCALL +MenuMenuBarCalcSize(HDC Dc, LPRECT Rect, PROSMENUINFO MenuInfo, HWND WndOwner) +{ + ROSMENUITEMINFO ItemInfo; + int Start, i, OrgX, OrgY, MaxY, HelpPos; + + if (NULL == Rect || NULL == MenuInfo) + { + return; + } + if (0 == MenuInfo->MenuItemCount) + { + return; + } + + DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n", + Rrect->left, Rect->top, Rect->right, Rect->bottom); + MenuInfo->Width = Rect->right - Rect->left; + MenuInfo->Height = 0; + MaxY = Rect->top + 1; + Start = 0; + HelpPos = -1; + MenuInitRosMenuItemInfo(&ItemInfo); + while (Start < MenuInfo->MenuItemCount) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + OrgX = Rect->left; + OrgY = MaxY; + + /* Parse items until line break or end of menu */ + for (i = Start; i < MenuInfo->MenuItemCount; i++) + { + if (-1 == HelpPos && 0 != (ItemInfo.fType & MF_RIGHTJUSTIFY)) + { + HelpPos = i; + } + if (i != Start && + 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) + { + break; + } + + DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX, OrgY); + MenuCalcItemSize(Dc, &ItemInfo, WndOwner, OrgX, OrgY, TRUE); + if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + + if (ItemInfo.Rect.right > Rect->right) + { + if (i != Start) + { + break; + } + else + { + ItemInfo.Rect.right = Rect->right; + } + } + MaxY = max(MaxY, ItemInfo.Rect.bottom ); + OrgX = ItemInfo.Rect.right; + if (i + 1 < MenuInfo->MenuItemCount) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + } + + /* Finish the line (set all items to the largest height found) */ + while (Start < i) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo)) + { + ItemInfo.Rect.bottom = MaxY; + MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo); + } + Start++; + } + } + + Rect->bottom = MaxY; + MenuInfo->Height = Rect->bottom - Rect->top; + MenuSetRosMenuInfo(MenuInfo); + + if (-1 != HelpPos) + { + /* Flush right all items between the MF_RIGHTJUSTIFY and */ + /* the last item (if several lines, only move the last line) */ + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + OrgY = ItemInfo.Rect.top; + OrgX = Rect->right; + for (i = MenuInfo->MenuItemCount - 1; HelpPos <= i; i--) + { + if (i < HelpPos) + { + break; /* done */ + } + if (ItemInfo.Rect.top != OrgY) + { + break; /* Other line */ + } + if (OrgX <= ItemInfo.Rect.right) + { + break; /* Too far right already */ + } + ItemInfo.Rect.left += OrgX - ItemInfo.Rect.right; + ItemInfo.Rect.right = OrgX; + OrgX = ItemInfo.Rect.left; + MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo); + if (HelpPos + 1 <= i && + ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + } + + MenuCleanupRosMenuItemInfo(&ItemInfo); +} +#endif + + UINT MenuDrawMenuBar(HDC hDC, LPRECT Rect, HWND hWnd, BOOL Draw) { @@ -513,16 +1551,1198 @@ MenuDrawMenuBar(HDC hDC, LPRECT Rect, HWND hWnd, BOOL Draw) return max(bottom - Rect->top, GetSystemMetrics(SM_CYMENU)); } +/*********************************************************************** + * MenuInitTracking + */ +static BOOL FASTCALL +MenuInitTracking(HWND Wnd, HMENU Menu, BOOL Popup, UINT Flags) +{ + DPRINT("Wnd=%p Menu=%p\n", Wnd, Menu); + + HideCaret(0); + + /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */ + if (0 == (Flags & TPM_NONOTIFY)) + { + SendMessageW(Wnd, WM_ENTERMENULOOP, Popup, 0); + } + + SendMessageW(Wnd, WM_SETCURSOR, (WPARAM) Wnd, HTCAPTION); + + if (0 == (Flags & TPM_NONOTIFY)) + { + ROSMENUINFO MenuInfo; + + SendMessageW(Wnd, WM_INITMENU, (WPARAM)Menu, 0); + + if (MenuGetRosMenuInfo(&MenuInfo, Menu) && 0 == MenuInfo.Height) + { + /* app changed/recreated menu bar entries in WM_INITMENU + Recalculate menu sizes else clicks will not work */ + SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | + SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED ); + + } + } + + return TRUE; +} + + +/*********************************************************************** + * MenuShowPopup + * + * Display a popup menu. + */ +static BOOL FASTCALL +MenuShowPopup(HWND WndOwner, HMENU Menu, UINT Id, + INT X, INT Y, INT XAnchor, INT YAnchor ) +{ + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + UINT Width, Height; + + DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n", + WndOwner, Menu, Id, X, Y, XAnchor, YAnchor); + + if (! MenuGetRosMenuInfo(&MenuInfo, Menu)) + { + return FALSE; + } + + if (NO_SELECTED_ITEM != MenuInfo.FocusedItem) + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo)) + { + ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); + MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo); + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuInfo.FocusedItem = NO_SELECTED_ITEM; + } + + /* store the owner for DrawItem */ + MenuInfo.WndOwner = WndOwner; + MenuSetRosMenuInfo(&MenuInfo); + + MenuPopupMenuCalcSize(&MenuInfo, WndOwner); + + /* adjust popup menu pos so that it fits within the desktop */ + + Width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER); + Height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER); + + if (GetSystemMetrics(SM_CXSCREEN ) < X + Width) + { + if (0 != XAnchor) + { + X -= Width - XAnchor; + } + if (GetSystemMetrics(SM_CXSCREEN) < X + Width) + { + X = GetSystemMetrics(SM_CXSCREEN) - Width; + } + } + if (X < 0 ) + { + X = 0; + } + + if (GetSystemMetrics(SM_CYSCREEN) < Y + Height) + { + if (0 != YAnchor) + { + Y -= Height + YAnchor; + } + if (GetSystemMetrics(SM_CYSCREEN) < Y + Height) + { + Y = GetSystemMetrics(SM_CYSCREEN) - Height; + } + } + if (Y < 0 ) + { + Y = 0; + } + + + /* NOTE: In Windows, top menu popup is not owned. */ + MenuInfo.Wnd = CreateWindowExW(0, POPUPMENU_CLASS_ATOMW, NULL, + WS_POPUP, X, Y, Width, Height, + WndOwner, 0, (HINSTANCE) GetWindowLongW(WndOwner, GWL_HINSTANCE), + (LPVOID) MenuInfo.Self); + if (NULL == MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) + { + return FALSE; + } + if (NULL == TopPopup) + { + TopPopup = MenuInfo.Wnd; + } + + /* Display the window */ + SetWindowPos(MenuInfo.Wnd, HWND_TOP, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + UpdateWindow(MenuInfo.Wnd); + + return TRUE; +} + +/*********************************************************************** + * MenuFindSubMenu + * + * Find a Sub menu. Return the position of the submenu, and modifies + * *hmenu in case it is found in another sub-menu. + * If the submenu cannot be found, NO_SELECTED_ITEM is returned. + */ +static UINT FASTCALL +MenuFindSubMenu(HMENU *Menu, HMENU SubTarget) +{ + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + UINT i; + HMENU SubMenu; + UINT Pos; + + if ((HMENU) 0xffff == *Menu + || ! MenuGetRosMenuInfo(&MenuInfo, *Menu)) + { + return NO_SELECTED_ITEM; + } + + MenuInitRosMenuItemInfo(&ItemInfo); + for (i = 0; i < MenuInfo.MenuItemCount; i++) + { + if (! MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return NO_SELECTED_ITEM; + } + if (0 == (ItemInfo.fType & MF_POPUP)) + { + continue; + } + if (ItemInfo.hSubMenu == SubTarget) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return i; + } + SubMenu = ItemInfo.hSubMenu; + Pos = MenuFindSubMenu(&SubMenu, SubTarget); + if (NO_SELECTED_ITEM != Pos) + { + *Menu = SubMenu; + return Pos; + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + + return NO_SELECTED_ITEM; +} + +/*********************************************************************** + * MenuSelectItem + */ +static void FASTCALL +MenuSelectItem(HWND WndOwner, PROSMENUINFO MenuInfo, UINT Index, + BOOL SendMenuSelect, HMENU TopMenu) +{ + HDC Dc; + ROSMENUITEMINFO ItemInfo; + ROSMENUINFO TopMenuInfo; + int Pos; + + DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner, MenuInfo, Index, SendMenuSelect); + + if (NULL == MenuInfo || 0 == MenuInfo->MenuItemCount || NULL == MenuInfo->Wnd) + { + return; + } + + if (MenuInfo->FocusedItem == Index) + { + return; + } + + if (0 != (MenuInfo->Flags & MF_POPUP)) + { + Dc = GetDC(MenuInfo->Wnd); + } + else + { + Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW); + } + + if (NULL == TopPopup) + { + TopPopup = MenuInfo->Wnd; + } + + SelectObject(Dc, hMenuFont); + MenuInitRosMenuItemInfo(&ItemInfo); + + /* Clear previous highlighted item */ + if (NO_SELECTED_ITEM != MenuInfo->FocusedItem) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)) + { + ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo); + } + MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, + MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP), + ODA_SELECT); + } + + /* Highlight new item (if any) */ + MenuInfo->FocusedItem = Index; + MenuSetRosMenuInfo(MenuInfo); + if (NO_SELECTED_ITEM != MenuInfo->FocusedItem) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)) + { + if (0 == (ItemInfo.fType & MF_SEPARATOR)) + { + ItemInfo.fState |= MF_HILITE; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo); + MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, + &ItemInfo, MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP), + ODA_SELECT); + } + if (SendMenuSelect) + { + SendMessageW(WndOwner, WM_MENUSELECT, + MAKELONG(ItemInfo.fType & MF_POPUP ? Index : ItemInfo.wID, + ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT | + (MenuInfo->Flags & MF_SYSMENU)), (LPARAM) MenuInfo->Self); + } + } + } + else if (SendMenuSelect) + { + if (NULL != TopMenu) + { + Pos = MenuFindSubMenu(&TopMenu, MenuInfo->Self); + if (NO_SELECTED_ITEM != Pos) + { + if (MenuGetRosMenuInfo(&TopMenuInfo, TopMenu) + && MenuGetRosMenuItemInfo(TopMenu, Pos, &ItemInfo)) + { + SendMessageW(WndOwner, WM_MENUSELECT, + MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState + | MF_MOUSESELECT + | (TopMenuInfo.Flags & MF_SYSMENU)), + (LPARAM) TopMenu); + } + } + } + } + + MenuCleanupRosMenuItemInfo(&ItemInfo); + ReleaseDC(MenuInfo->Wnd, Dc); +} + +/*********************************************************************** + * MenuMoveSelection + * + * Moves currently selected item according to the Offset parameter. + * If there is no selection then it should select the last item if + * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT. + */ +static void FASTCALL +MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset) +{ + INT i; + ROSMENUITEMINFO ItemInfo; + + DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, offset); + + MenuInitRosMenuItemInfo(&ItemInfo); + if (NO_SELECTED_ITEM != MenuInfo->FocusedItem) + { + if (1 == MenuInfo->MenuItemCount) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + else + { + for (i = MenuInfo->FocusedItem + Offset; + 0 <= i && i < MenuInfo->MenuItemCount; + i += Offset) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) && + 0 == (ItemInfo.fType & MF_SEPARATOR)) + { + MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL); + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + } + } + + for (i = (0 < Offset) ? 0 : MenuInfo->MenuItemCount - 1; + 0 <= i && i < MenuInfo->MenuItemCount; i += Offset) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) && + 0 == (ItemInfo.fType & MF_SEPARATOR)) + { + MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL); + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + + MenuCleanupRosMenuItemInfo(&ItemInfo); +} + +/*********************************************************************** + * MenuShowSubPopup + * + * Display the sub-menu of the selected item of this menu. + * Return the handle of the submenu, or menu if no submenu to display. + */ +static HMENU FASTCALL +MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags) +{ + RECT Rect; + ROSMENUITEMINFO ItemInfo; + HDC Dc; + HMENU Ret; + + DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst); + + if (NO_SELECTED_ITEM == MenuInfo->FocusedItem) + { + return MenuInfo->Self; + } + + MenuInitRosMenuItemInfo(&ItemInfo); + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + + /* message must be sent before using item, + because nearly everything may be changed by the application ! */ + + /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ + if (0 == (Flags & TPM_NONOTIFY)) + { + SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu, + MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo))); + } + + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + Rect = ItemInfo.Rect; + + /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ + if (0 == (ItemInfo.fState & MF_HILITE)) + { + if (0 != (MenuInfo->Flags & MF_POPUP)) + { + Dc = GetDC(MenuInfo->Wnd); + } + else + { + Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW); + } + + SelectObject(Dc, hMenuFont); + + ItemInfo.fState |= MF_HILITE; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo); + MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height, + ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE); + ReleaseDC(MenuInfo->Wnd, Dc); + } + + if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left + && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right) + { + ItemInfo.Rect = Rect; + } + + ItemInfo.fState |= MF_MOUSESELECT; + + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo); + + if (IS_SYSTEM_MENU(MenuInfo)) + { +#if 0 /* FIXME */ + MENU_InitSysMenuPopup(item->hSubMenu, + GetWindowLongW( menu->hWnd, GWL_STYLE ), + GetClassLongW( menu->hWnd, GCL_STYLE)); + + NC_GetSysPopupPos( menu->hWnd, &rect ); + rect.top = rect.bottom; + rect.right = GetSystemMetrics(SM_CXSIZE); + rect.bottom = GetSystemMetrics(SM_CYSIZE); +#endif + } + else + { + GetWindowRect(MenuInfo->Wnd, &Rect); + if (0 != (MenuInfo->Flags & MF_POPUP)) + { + Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER); + Rect.top += ItemInfo.Rect.top; + Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER); + Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom; + } + else + { + Rect.left += ItemInfo.Rect.left; + Rect.top += ItemInfo.Rect.bottom; + Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left; + Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top; + } + } + + MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, + Rect.left, Rect.top, Rect.right, Rect.bottom ); + if (SelectFirst) + { + MenuMoveSelection(WndOwner, ItemInfo.hSubMenu, ITEM_NEXT); + } + + Ret = ItemInfo.hSubMenu; + MenuCleanupRosMenuItemInfo(&ItemInfo); + + return Ret; +} + +/*********************************************************************** + * MenuHideSubPopups + * + * Hide the sub-popup menus of this menu. + */ +static void FASTCALL +MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SendMenuSelect) +{ + ROSMENUINFO SubMenuInfo; + ROSMENUITEMINFO ItemInfo; + + DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect); + + if (NULL != MenuInfo && NULL != TopPopup && NO_SELECTED_ITEM != MenuInfo->FocusedItem) + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo) + || 0 == (ItemInfo.fType & MF_POPUP) + || 0 == (ItemInfo.fState & MF_MOUSESELECT)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + ItemInfo.fState &= ~MF_MOUSESELECT; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo); + if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu)) + { + MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE); + MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL); + DestroyWindow(SubMenuInfo.Wnd); + SubMenuInfo.Wnd = NULL; + MenuSetRosMenuInfo(&SubMenuInfo); + } + } +} + +/*********************************************************************** + * MenuSwitchTracking + * + * Helper function for menu navigation routines. + */ +static void FASTCALL +MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index) +{ + ROSMENUINFO TopMenuInfo; + + DPRINT("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index); + + if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) && + Mt->TopMenu != PtMenuInfo->Self && + 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP)) + { + /* both are top level menus (system and menu-bar) */ + MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE); + MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL); + Mt->TopMenu = PtMenuInfo->Self; + } + else + { + MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE); + } + + MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL); +} + +/*********************************************************************** + * MenuExecFocusedItem + * + * Execute a menu item (for instance when user pressed Enter). + * Return the wID of the executed item. Otherwise, -1 indicating + * that no menu item was executed; + * Have to receive the flags for the TrackPopupMenu options to avoid + * sending unwanted message. + * + */ +static INT FASTCALL +MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags) +{ + ROSMENUITEMINFO ItemInfo; + UINT wID; + + DPRINT("%p menu=%p\n", Mt, MenuInfo); + + if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem) + { + return -1; + } + + MenuInitRosMenuItemInfo(&ItemInfo); + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return -1; + } + + DPRINT("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu); + + if (0 == (ItemInfo.fType & MF_POPUP)) + { + if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)) + && 0 == (ItemInfo.fType & MF_SEPARATOR)) + { + /* If TPM_RETURNCMD is set you return the id, but + do not send a message to the owner */ + if (0 == (Flags & TPM_RETURNCMD)) + { + if (0 != (MenuInfo->Flags & MF_SYSMENU)) + { + PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID, + MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y)); + } + else + { + PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0); + } + } + wID = ItemInfo.wID; + MenuCleanupRosMenuItemInfo(&ItemInfo); + return wID; + } + } + else + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags); + } + + return -1; +} + +/*********************************************************************** + * MenuButtonDown + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL +MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags) +{ + int Index; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO Item; + + DPRINT("%x PtMenu=%p\n", Mt, PtMenu); + + if (NULL != PtMenu) + { + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return FALSE; + } + if (IS_SYSTEM_MENU(&MenuInfo)) + { + Index = 0; + } + else + { + Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y); + } + MenuInitRosMenuItemInfo(&Item); + if (Index < 0 || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item)) + { + MenuCleanupRosMenuItemInfo(&Item); + return FALSE; + } + + if (MenuInfo.FocusedItem != Index) + { + MenuSwitchTracking(Mt, &MenuInfo, Index); + } + + /* If the popup menu is not already "popped" */ + if (0 == (Item.fState & MF_MOUSESELECT)) + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags); + } + + MenuCleanupRosMenuItemInfo(&Item); + + return TRUE; + } + + /* else the click was on the menu bar, finish the tracking */ + + return FALSE; +} + +/*********************************************************************** + * MenuButtonUp + * + * Return the value of MenuExecFocusedItem if + * the selected item was not a popup. Else open the popup. + * A -1 return value indicates that we go on with menu tracking. + * + */ +static INT FASTCALL +MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags) +{ + UINT Id; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + + DPRINT("%p hmenu=%x\n", Mt, PtMenu); + + if (NULL != PtMenu) + { + Id = 0; + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return -1; + } + + if (! IS_SYSTEM_MENU(&MenuInfo)) + { + Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y); + } + MenuInitRosMenuItemInfo(&ItemInfo); + if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) && + MenuInfo.FocusedItem == Id) + { + if (0 == (ItemInfo.fType & MF_POPUP)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuExecFocusedItem(Mt, &MenuInfo, Flags); + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + + /* If we are dealing with the top-level menu */ + /* and this is a click on an already "popped" item: */ + /* Stop the menu tracking and close the opened submenus */ + if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return 0; + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuInfo.TimeToHide = TRUE; + MenuSetRosMenuInfo(&MenuInfo); + } + + return -1; +} + +/*********************************************************************** + * MenuPtMenu + * + * Walks menu chain trying to find a menu pt maps to. + */ +static HMENU FASTCALL +MenuPtMenu(HMENU Menu, POINT Pt) +{ + extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + HMENU Ret = NULL; + INT Ht; + + if (! MenuGetRosMenuInfo(&MenuInfo, Menu)) + { + return NULL; + } + + /* try subpopup first (if any) */ + if (NO_SELECTED_ITEM != MenuInfo.FocusedItem) + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) && + 0 != (ItemInfo.fType & MF_POPUP) && + 0 != (ItemInfo.fState & MF_MOUSESELECT)) + { + Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt); + if (NULL != Ret) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return Ret; + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + } + + /* check the current window (avoiding WM_HITTEST) */ + Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt); + if (0 != (MenuInfo.Flags & MF_POPUP )) + { + if (HTNOWHERE != Ht && HTERROR != Ht) + { + Ret = Menu; + } + } + else if (HTSYSMENU == Ht) + { +#if 0 /* FIXME */ + Ret = get_win_sys_menu( menu->hWnd ); +#else + Ret = NULL; +#endif + } + else if (HTMENU == Ht) + { + Ret = GetMenu(MenuInfo.Wnd); + } + + return Ret; +} + +/*********************************************************************** + * MenuMouseMove + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL +MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags) +{ + UINT Index; + ROSMENUINFO MenuInfo; + + if (NULL != PtMenu) + { + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return TRUE; + } + if (IS_SYSTEM_MENU(&MenuInfo)) + { + Index = 0; + } + else + { + Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y); + } + } + else + { + Index = NO_SELECTED_ITEM; + } + + if (NO_SELECTED_ITEM == Index) + { + if (Mt->CurrentMenu == MenuInfo.Self || + MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)) + { + MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, + TRUE, Mt->TopMenu); + } + } + else if (MenuInfo.FocusedItem != Index) + { + MenuSwitchTracking(Mt, &MenuInfo, Index); + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags); + } + + return TRUE; +} + +/*********************************************************************** + * MenuTrackMenu + * + * Menu tracking code. + */ +static INT FASTCALL +MenuTrackMenu(HMENU Menu, UINT Flags, INT x, INT y, + HWND Wnd, const RECT *Rect ) +{ + MSG Msg; + ROSMENUINFO MenuInfo; + BOOL fRemove; + INT ExecutedMenuId = -1; + MTRACKER Mt; + BOOL EnterIdleSent = FALSE; + + Mt.TrackFlags = 0; + Mt.CurrentMenu = Menu; + Mt.TopMenu = Menu; + Mt.OwnerWnd = Wnd; + Mt.Pt.x = x; + Mt.Pt.y = y; + + DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n", + Menu, Flags, x, y, Wnd, Rect ? Rect->left : 0, Rect ? Rect->top : 0, + Rect ? Rect->right : 0, Rect ? Rect->bottom : 0); + + fEndMenu = FALSE; + if (! MenuGetRosMenuInfo(&MenuInfo, Menu)) + { + return FALSE; + } + + if (0 != (Flags & TPM_BUTTONDOWN)) + { + /* Get the result in order to start the tracking or not */ + fRemove = MenuButtonDown(&Mt, Menu, Flags); + fEndMenu = ! fRemove; + } + + SetCapture(Mt.OwnerWnd); + + while (! fEndMenu) + { + /* we have to keep the message in the queue until it's + * clear that menu loop is not over yet. */ + + for (;;) + { + if (PeekMessageW(&Msg, 0, 0, 0, PM_NOREMOVE)) + { + if (! CallMsgFilterW(&Msg, MSGF_MENU)) + { + break; + } + /* remove the message from the queue */ + PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE ); + } + else + { + if (! EnterIdleSent) + { + HWND Win = (0 != (Flags & TPM_ENTERIDLEEX) + && 0 != (MenuInfo.Flags & MF_POPUP)) ? MenuInfo.Wnd : NULL; + EnterIdleSent = TRUE; + SendMessageW(Mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) Win); + } + WaitMessage(); + } + } + + /* check if EndMenu() tried to cancel us, by posting this message */ + if (WM_CANCELMODE == Msg.message) + { + /* we are now out of the loop */ + fEndMenu = TRUE; + + /* remove the message from the queue */ + PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE); + + /* break out of internal loop, ala ESCAPE */ + break; + } + + TranslateMessage(&Msg); + Mt.Pt = Msg.pt; + + if (Msg.hwnd == MenuInfo.Wnd || WM_TIMER != Msg.message) + { + EnterIdleSent = FALSE; + } + + fRemove = FALSE; + if (WM_MOUSEFIRST <= Msg.message && Msg.message <= WM_MOUSELAST) + { + /* + * Use the mouse coordinates in lParam instead of those in the MSG + * struct to properly handle synthetic messages. They are already + * in screen coordinates. + */ + Mt.Pt.x = (short) LOWORD(Msg.lParam); + Mt.Pt.y = (short) HIWORD(Msg.lParam); + + /* Find a menu for this mouse event */ + Menu = MenuPtMenu(Mt.TopMenu, Mt.Pt); + + switch(Msg.message) + { + /* no WM_NC... messages in captured state */ + + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + if (0 == (Flags & TPM_RIGHTBUTTON)) + { + break; + } + /* fall through */ + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + /* If the message belongs to the menu, removes it from the queue */ + /* Else, end menu tracking */ + fRemove = MenuButtonDown(&Mt, Menu, Flags); + fEndMenu = ! fRemove; + break; + + case WM_RBUTTONUP: + if (0 == (Flags & TPM_RIGHTBUTTON)) + { + break; + } + /* fall through */ + case WM_LBUTTONUP: + /* Check if a menu was selected by the mouse */ + if (NULL != Menu) + { + ExecutedMenuId = MenuButtonUp(&Mt, Menu, Flags); + + /* End the loop if ExecutedMenuId is an item ID */ + /* or if the job was done (ExecutedMenuId = 0). */ + fEndMenu = fRemove = (-1 != ExecutedMenuId); + } + else + { + /* No menu was selected by the mouse */ + /* if the function was called by TrackPopupMenu, continue + with the menu tracking. If not, stop it */ + fEndMenu = (0 != (Flags & TPM_POPUPMENU) ? FALSE : TRUE); + } + break; + + case WM_MOUSEMOVE: + if (Menu) + { + fEndMenu |= ! MenuMouseMove(&Mt, Menu, Flags); + } + break; + + } /* switch(Msg.message) - mouse */ + } + else if (WM_KEYFIRST <= Msg.message && Msg.message <= WM_KEYLAST) + { +#if 0 /* FIXME */ + fRemove = TRUE; /* Keyboard messages are always removed */ + switch(msg.message) + { + case WM_KEYDOWN: + switch(msg.wParam) + { + case VK_HOME: + case VK_END: + MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, + NO_SELECTED_ITEM, FALSE, 0 ); + /* fall through */ + case VK_UP: + MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, + (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV ); + break; + + case VK_DOWN: /* If on menu bar, pull-down the menu */ + + menu = MENU_GetMenu( mt.hCurrentMenu ); + if (!(menu->wFlags & MF_POPUP)) + mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags); + else /* otherwise try to move selection */ + MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, ITEM_NEXT ); + break; + + case VK_LEFT: + MENU_KeyLeft( &mt, wFlags ); + break; + + case VK_RIGHT: + MENU_KeyRight( &mt, wFlags ); + break; + + case VK_ESCAPE: + fEndMenu = MENU_KeyEscape(&mt, wFlags); + break; + + case VK_F1: + { + HELPINFO hi; + hi.cbSize = sizeof(HELPINFO); + hi.iContextType = HELPINFO_MENUITEM; + if (menu->FocusedItem == NO_SELECTED_ITEM) + hi.iCtrlId = 0; + else + hi.iCtrlId = menu->items[menu->FocusedItem].wID; + hi.hItemHandle = hmenu; + hi.dwContextId = menu->dwContextHelpID; + hi.MousePos = msg.pt; + SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi); + break; + } + + default: + break; + } + break; /* WM_KEYDOWN */ + + case WM_SYSKEYDOWN: + switch(msg.wParam) + { + case VK_MENU: + fEndMenu = TRUE; + break; + + } + break; /* WM_SYSKEYDOWN */ + + case WM_CHAR: + { + UINT pos; + + if (msg.wParam == '\r' || msg.wParam == ' ') + { + ExecutedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags); + fEndMenu = (ExecutedMenuId != -1); + + break; + } + + /* Hack to avoid control chars. */ + /* We will find a better way real soon... */ + if (msg.wParam < 32) break; + + pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu, + LOWORD(msg.wParam), FALSE ); + if (pos == (UINT)-2) fEndMenu = TRUE; + else if (pos == (UINT)-1) MessageBeep(0); + else + { + MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos, + TRUE, 0 ); + ExecutedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags); + fEndMenu = (ExecutedMenuId != -1); + } + } + break; + } /* switch(msg.message) - kbd */ +#endif + } + else + { + DispatchMessageW(&Msg); + } + + if (! fEndMenu) + { + fRemove = TRUE; + } + + /* finally remove message from the queue */ + + if (fRemove && 0 == (Mt.TrackFlags & TF_SKIPREMOVE)) + { + PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE); + } + else + { + Mt.TrackFlags &= ~TF_SKIPREMOVE; + } + } + + SetCapture(NULL); /* release the capture */ + + /* If dropdown is still painted and the close box is clicked on + then the menu will be destroyed as part of the DispatchMessage above. + This will then invalidate the menu handle in Mt.hTopMenu. We should + check for this first. */ + if (IsMenu(Mt.TopMenu)) + { + if (IsWindow(Mt.OwnerWnd)) + { + if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu)) + { + MenuHideSubPopups(Mt.OwnerWnd, &MenuInfo, FALSE); + + if (0 != (MenuInfo.Flags & MF_POPUP)) + { + DestroyWindow(MenuInfo.Wnd); + MenuInfo.Wnd = NULL; + } + MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, NULL); + } + + SendMessageW(Mt.OwnerWnd, WM_MENUSELECT, MAKELONG(0, 0xffff), 0); + } + + if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu)) + { + /* Reset the variable for hiding menu */ + MenuInfo.TimeToHide = FALSE; + MenuSetRosMenuInfo(&MenuInfo); + } + } + + /* The return value is only used by TrackPopupMenu */ + return (-1 != ExecutedMenuId) ? ExecutedMenuId : 0; +} + +/*********************************************************************** + * MenuExitTracking + */ +static BOOL FASTCALL +MenuExitTracking(HWND Wnd) +{ + DPRINT("hwnd=%p\n", hWnd); + + SendMessageW(Wnd, WM_EXITMENULOOP, 0, 0); + ShowCaret(0); + return TRUE; +} + VOID -MenuTrackMouseMenuBar(HWND hWnd, ULONG Ht, POINT Pt) +MenuTrackMouseMenuBar(HWND Wnd, ULONG Ht, POINT Pt) { - int Item = NtUserMenuItemFromPoint(hWnd, GetMenu(hWnd), Pt.x, Pt.y); - - if(Item > -1) - { - /* FIXME */ - } +#if 0 /* FIXME */ + HMENU Menu = (ht == HTSYSMENU) ? get_win_sys_menu( Wnd ) : GetMenu( Wnd ); + UINT Flags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON; +#else + HMENU Menu; + UINT Flags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON; + + if (HTSYSMENU == Ht) + { + return; + } + Menu = GetMenu(Wnd); +#endif + + DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd, Ht, pt.x, pt.y); + + if (IsMenu(Menu)) + { + /* map point to parent client coordinates */ + HWND Parent = GetAncestor(Wnd, GA_PARENT ); + if (Parent != GetDesktopWindow()) + { + ScreenToClient(Parent, &Pt); + } + + MenuInitTracking(Wnd, Menu, FALSE, Flags); + MenuTrackMenu(Menu, Flags, Pt.x, Pt.y, Wnd, NULL); + MenuExitTracking(Wnd); + } } @@ -536,7 +2756,7 @@ MenuTrackKbdMenuBar(HWND hWnd, ULONG wParam, ULONG Key) /*static BOOL MenuIsStringItem(ULONG TypeData) { - return((TypeData & MENU_TYPE_MASK) == MF_STRING); + return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType)); }*/ @@ -729,7 +2949,7 @@ BOOL STDCALL GetMenuInfo(HMENU hmenu, LPMENUINFO lpcmi) { - MENUINFO mi; + ROSMENUINFO mi; BOOL res = FALSE; if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) @@ -763,18 +2983,24 @@ UINT STDCALL GetMenuItemID(HMENU hMenu, int nPos) { - MENUITEMINFOW mii; + ROSMENUITEMINFO mii; mii.cbSize = sizeof(MENUITEMINFOW); mii.fMask = MIIM_ID | MIIM_SUBMENU; - if(!NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE)) - { - return -1; - } + if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE)) + { + return -1; + } - if(mii.hSubMenu) return -1; - if(mii.wID == 0) return -1; + if (NULL != mii.hSubMenu) + { + return -1; + } + if (0 == mii.wID) + { + return -1; + } return mii.wID; } @@ -835,28 +3061,29 @@ GetMenuState( UINT uId, UINT uFlags) { - MENUITEMINFOW mii; + ROSMENUITEMINFO mii; + mii.cbSize = sizeof(MENUITEMINFOW); mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU; SetLastError(0); if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE)) - { - UINT nSubItems = 0; - if(mii.hSubMenu) { - nSubItems = (UINT)NtUserBuildMenuItemList(mii.hSubMenu, NULL, 0, 0); + UINT nSubItems = 0; + if(mii.hSubMenu) + { + nSubItems = (UINT)NtUserBuildMenuItemList(mii.hSubMenu, NULL, 0, 0); - /* FIXME - ported from wine, does that work (0xff)? */ - if(GetLastError() != ERROR_INVALID_MENU_HANDLE) - return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff); + /* FIXME - ported from wine, does that work (0xff)? */ + if(GetLastError() != ERROR_INVALID_MENU_HANDLE) + return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff); - return (UINT)-1; /* Invalid submenu */ - } + return (UINT)-1; /* Invalid submenu */ + } - /* FIXME - ported from wine, does that work? */ - return (mii.fType | mii.fState); - } + /* FIXME - ported from wine, does that work? */ + return (mii.fType | mii.fState); + } return (UINT)-1; } @@ -905,13 +3132,16 @@ GetSubMenu( HMENU hMenu, int nPos) { - MENUITEMINFOW mi; - mi.cbSize = sizeof(mi); + ROSMENUITEMINFO mi; + + mi.cbSize = sizeof(MENUITEMINFOW); mi.fMask = MIIM_SUBMENU; - if(NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE)) - { - return mi.hSubMenu; - } + + if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE)) + { + return mi.hSubMenu; + } + return (HMENU)0; } @@ -1140,8 +3370,10 @@ InsertMenuW( } mii.dwTypeData = (LPWSTR)lpNewItem; + mii.cch = (NULL == lpNewItem ? 0 : wcslen(lpNewItem)); if(uFlags & MF_POPUP) { + mii.fType |= MF_POPUP; mii.fMask |= MIIM_SUBMENU; mii.hSubMenu = (HMENU)uIDNewItem; } @@ -1162,10 +3394,7 @@ STDCALL IsMenu( HMENU hMenu) { - DWORD ret; - SetLastError(ERROR_SUCCESS); - ret = NtUserBuildMenuItemList(hMenu, NULL, 0, 0); - return ((ret == (DWORD)-1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE)); + return 0 <= NtUserBuildMenuItemList(hMenu, NULL, 0, 0); } @@ -1349,7 +3578,7 @@ SetMenuInfo( HMENU hmenu, LPCMENUINFO lpcmi) { - MENUINFO mi; + ROSMENUINFO mi; BOOL res = FALSE; if(lpcmi->cbSize != sizeof(MENUINFO)) return res; @@ -1471,8 +3700,8 @@ DWORD STDCALL GetMenuContextHelpId(HMENU hmenu) { - MENUINFO mi; - mi.cbSize = sizeof(MENUINFO); + ROSMENUINFO mi; + mi.cbSize = sizeof(ROSMENUINFO); mi.fMask = MIM_HELPID; if(NtUserMenuInfo(hmenu, &mi, FALSE)) diff --git a/reactos/subsys/win32k/include/menu.h b/reactos/subsys/win32k/include/menu.h index 70ec63c1f36..83ce0984df6 100644 --- a/reactos/subsys/win32k/include/menu.h +++ b/reactos/subsys/win32k/include/menu.h @@ -3,6 +3,7 @@ #include #include +#include #define IS_ATOM(x) \ (((ULONG_PTR)(x) > 0x0) && ((ULONG_PTR)(x) < 0x10000)) @@ -27,21 +28,17 @@ typedef struct _MENU_ITEM UNICODE_STRING Text; HBITMAP hbmpItem; RECT Rect; + UINT XTab; } MENU_ITEM, *PMENU_ITEM; typedef struct _MENU_OBJECT { - HANDLE Self; PW32PROCESS W32Process; LIST_ENTRY ListEntry; - int MenuItemCount; FAST_MUTEX MenuItemsLock; PMENU_ITEM MenuItemList; - MENUINFO MenuInfo; + ROSMENUINFO MenuInfo; BOOL RtoL; - BOOL IsSystemMenu; - BOOL IsMenuBar; - int Height; } MENU_OBJECT, *PMENU_OBJECT; PMENU_OBJECT FASTCALL @@ -77,27 +74,17 @@ BOOL FASTCALL IntSetMenuContextHelpId(PMENU_OBJECT MenuObject, DWORD dwContextHelpId); BOOL FASTCALL -IntGetMenuInfo(PMENU_OBJECT MenuObject, LPMENUINFO lpmi); +IntGetMenuInfo(PMENU_OBJECT MenuObject, PROSMENUINFO lpmi); BOOL FASTCALL IntIsMenu(HMENU hMenu); BOOL FASTCALL -IntSetMenuInfo(PMENU_OBJECT MenuObject, LPMENUINFO lpmi); +IntSetMenuInfo(PMENU_OBJECT MenuObject, PROSMENUINFO lpmi); int FASTCALL IntGetMenuItemByFlag(PMENU_OBJECT MenuObject, UINT uSearchBy, UINT fFlag, PMENU_ITEM *MenuItem, PMENU_ITEM *PrevMenuItem); - -BOOL FASTCALL -IntGetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, LPMENUITEMINFOW lpmii); - -BOOL FASTCALL -IntSetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, LPCMENUITEMINFOW lpmii); - -BOOL FASTCALL -IntInsertMenuItem(PMENU_OBJECT MenuObject, UINT uItem, BOOL fByPosition, - LPCMENUITEMINFOW lpmii); UINT FASTCALL IntEnableMenuItem(PMENU_OBJECT MenuObject, UINT uIDEnableItem, UINT uEnable); diff --git a/reactos/subsys/win32k/ntuser/menu.c b/reactos/subsys/win32k/ntuser/menu.c index 472fb296594..d8625ed601a 100644 --- a/reactos/subsys/win32k/ntuser/menu.c +++ b/reactos/subsys/win32k/ntuser/menu.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: menu.c,v 1.43 2004/01/26 23:22:48 weiden Exp $ +/* $Id: menu.c,v 1.44 2004/02/15 07:39:12 gvg Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -71,6 +71,11 @@ } else { \ (state) &= ~MFS_DEFAULT; \ } \ + if((change) & MF_MOUSESELECT) { \ + (state) |= MF_MOUSESELECT; \ + } else { \ + (state) &= ~MF_MOUSESELECT; \ + } \ } #define FreeMenuText(MenuItem) \ @@ -171,7 +176,7 @@ IntFreeMenuItem(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, if(RemoveFromList) { /* FIXME - Remove from List */ - MenuObject->MenuItemCount--; + MenuObject->MenuInfo.MenuItemCount--; } if(bRecurse && MenuItem->hSubMenu) { @@ -225,7 +230,7 @@ IntDeleteMenuItems(PMENU_OBJECT MenuObject, BOOL bRecurse) CurItem = NextItem; res++; } - MenuObject->MenuItemCount = 0; + MenuObject->MenuInfo.MenuItemCount = 0; MenuObject->MenuItemList = NULL; return res; } @@ -248,7 +253,7 @@ IntDestroyMenuObject(PMENU_OBJECT MenuObject, ExReleaseFastMutexUnsafe(&MenuObject->W32Process->MenuListLock); } - ObmCloseHandle(MenuObject->W32Process->WindowStation->HandleTable, MenuObject->Self); + ObmCloseHandle(MenuObject->W32Process->WindowStation->HandleTable, MenuObject->MenuInfo.Self); return TRUE; } @@ -271,8 +276,6 @@ IntCreateMenu(PHANDLE Handle, BOOL IsMenuBar) return NULL; } - MenuObject->Self = *Handle; - MenuObject->IsMenuBar = IsMenuBar; MenuObject->W32Process = Win32Process; MenuObject->RtoL = FALSE; /* default */ MenuObject->MenuInfo.cbSize = sizeof(MENUINFO); /* not used */ @@ -283,8 +286,16 @@ IntCreateMenu(PHANDLE Handle, BOOL IsMenuBar) NtGdiCreateSolidBrush(RGB(192, 192, 192)); /* FIXME: default background color */ MenuObject->MenuInfo.dwContextHelpID = 0; /* default */ MenuObject->MenuInfo.dwMenuData = 0; /* default */ + MenuObject->MenuInfo.Self = *Handle; + MenuObject->MenuInfo.FocusedItem = NO_SELECTED_ITEM; + MenuObject->MenuInfo.Flags = (IsMenuBar ? 0 : MF_POPUP); + MenuObject->MenuInfo.Wnd = NULL; + MenuObject->MenuInfo.WndOwner = NULL; + MenuObject->MenuInfo.Height = 0; + MenuObject->MenuInfo.Width = 0; + MenuObject->MenuInfo.TimeToHide = FALSE; - MenuObject->MenuItemCount = 0; + MenuObject->MenuInfo.MenuItemCount = 0; MenuObject->MenuItemList = NULL; ExInitializeFastMutex(&MenuObject->MenuItemsLock); @@ -302,7 +313,7 @@ IntCloneMenuItems(PMENU_OBJECT Destination, PMENU_OBJECT Source) PMENU_ITEM MenuItem, NewMenuItem = NULL; PMENU_ITEM Old = NULL; - if(!Source->MenuItemCount) + if(!Source->MenuInfo.MenuItemCount) return FALSE; ExAcquireFastMutexUnsafe(&Destination->MenuItemsLock); @@ -354,7 +365,7 @@ IntCloneMenuItems(PMENU_OBJECT Destination, PMENU_OBJECT Source) Old->Next = NewMenuItem; else Destination->MenuItemList = NewMenuItem; - Destination->MenuItemCount++; + Destination->MenuInfo.MenuItemCount++; MenuItem = MenuItem->Next; } @@ -379,7 +390,6 @@ IntCloneMenu(PMENU_OBJECT Source) if(!MenuObject) return NULL; - MenuObject->Self = Handle; MenuObject->W32Process = Process; MenuObject->RtoL = Source->RtoL; MenuObject->MenuInfo.cbSize = sizeof(MENUINFO); /* not used */ @@ -389,8 +399,15 @@ IntCloneMenu(PMENU_OBJECT Source) MenuObject->MenuInfo.hbrBack = Source->MenuInfo.hbrBack; MenuObject->MenuInfo.dwContextHelpID = Source->MenuInfo.dwContextHelpID; MenuObject->MenuInfo.dwMenuData = Source->MenuInfo.dwMenuData; + MenuObject->MenuInfo.Self = Handle; + MenuObject->MenuInfo.FocusedItem = NO_SELECTED_ITEM; + MenuObject->MenuInfo.Wnd = NULL; + MenuObject->MenuInfo.WndOwner = NULL; + MenuObject->MenuInfo.Height = 0; + MenuObject->MenuInfo.Width = 0; + MenuObject->MenuInfo.TimeToHide = FALSE; - MenuObject->MenuItemCount = 0; + MenuObject->MenuInfo.MenuItemCount = 0; MenuObject->MenuItemList = NULL; ExInitializeFastMutex(&MenuObject->MenuItemsLock); @@ -419,23 +436,26 @@ IntSetMenuContextHelpId(PMENU_OBJECT MenuObject, DWORD dwContextHelpId) } BOOL FASTCALL -IntGetMenuInfo(PMENU_OBJECT MenuObject, LPMENUINFO lpmi) +IntGetMenuInfo(PMENU_OBJECT MenuObject, PROSMENUINFO lpmi) { - if(MenuObject) - { - if(lpmi->fMask & MIM_BACKGROUND) - lpmi->hbrBack = MenuObject->MenuInfo.hbrBack; - if(lpmi->fMask & MIM_HELPID) - lpmi->dwContextHelpID = MenuObject->MenuInfo.dwContextHelpID; - if(lpmi->fMask & MIM_MAXHEIGHT) - lpmi->cyMax = MenuObject->MenuInfo.cyMax; - if(lpmi->fMask & MIM_MENUDATA) - lpmi->dwMenuData = MenuObject->MenuInfo.dwMenuData; - if(lpmi->fMask & MIM_STYLE) - lpmi->dwStyle = MenuObject->MenuInfo.dwStyle; - return TRUE; - } - return FALSE; + if(lpmi->fMask & MIM_BACKGROUND) + lpmi->hbrBack = MenuObject->MenuInfo.hbrBack; + if(lpmi->fMask & MIM_HELPID) + lpmi->dwContextHelpID = MenuObject->MenuInfo.dwContextHelpID; + if(lpmi->fMask & MIM_MAXHEIGHT) + lpmi->cyMax = MenuObject->MenuInfo.cyMax; + if(lpmi->fMask & MIM_MENUDATA) + lpmi->dwMenuData = MenuObject->MenuInfo.dwMenuData; + if(lpmi->fMask & MIM_STYLE) + lpmi->dwStyle = MenuObject->MenuInfo.dwStyle; + if (sizeof(MENUINFO) < lpmi->cbSize) + { + RtlCopyMemory((char *) lpmi + sizeof(MENUINFO), + (char *) &MenuObject->MenuInfo + sizeof(MENUINFO), + lpmi->cbSize - sizeof(MENUINFO)); + } + + return TRUE; } @@ -454,7 +474,7 @@ IntIsMenu(HMENU hMenu) BOOL FASTCALL -IntSetMenuInfo(PMENU_OBJECT MenuObject, LPMENUINFO lpmi) +IntSetMenuInfo(PMENU_OBJECT MenuObject, PROSMENUINFO lpmi) { if(lpmi->fMask & MIM_BACKGROUND) MenuObject->MenuInfo.hbrBack = lpmi->hbrBack; @@ -467,9 +487,19 @@ IntSetMenuInfo(PMENU_OBJECT MenuObject, LPMENUINFO lpmi) if(lpmi->fMask & MIM_STYLE) MenuObject->MenuInfo.dwStyle = lpmi->dwStyle; if(lpmi->fMask & MIM_APPLYTOSUBMENUS) - { + { /* FIXME */ - } + } + if (sizeof(MENUINFO) < lpmi->cbSize) + { + MenuObject->MenuInfo.FocusedItem = lpmi->FocusedItem; + MenuObject->MenuInfo.Height = lpmi->Height; + MenuObject->MenuInfo.Width = lpmi->Width; + MenuObject->MenuInfo.Wnd = lpmi->Wnd; + MenuObject->MenuInfo.WndOwner = lpmi->WndOwner; + MenuObject->MenuInfo.TimeToHide = lpmi->TimeToHide; + } + return TRUE; } @@ -582,56 +612,86 @@ IntInsertMenuItemToList(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, int pos) MenuItem->Next = NULL; } } - MenuObject->MenuItemCount++; + MenuObject->MenuInfo.MenuItemCount++; return npos; } BOOL FASTCALL -IntGetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, LPMENUITEMINFOW lpmii) +IntGetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, PROSMENUITEMINFO lpmii) { - if(!MenuItem || !MenuObject || !lpmii) - { - return FALSE; - } - - lpmii->cch = MenuItem->Text.Length; + UNICODE_STRING Text; + NTSTATUS Status; + + lpmii->cch = MenuItem->Text.Length / sizeof(WCHAR); if(lpmii->fMask & MIIM_BITMAP) - { - lpmii->hbmpItem = MenuItem->hbmpItem; - } + { + lpmii->hbmpItem = MenuItem->hbmpItem; + } if(lpmii->fMask & MIIM_CHECKMARKS) - { - lpmii->hbmpChecked = MenuItem->hbmpChecked; - lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked; - } + { + lpmii->hbmpChecked = MenuItem->hbmpChecked; + lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked; + } if(lpmii->fMask & MIIM_DATA) - { - lpmii->dwItemData = MenuItem->dwItemData; - } + { + lpmii->dwItemData = MenuItem->dwItemData; + } if(lpmii->fMask & (MIIM_FTYPE | MIIM_TYPE)) - { - lpmii->fType = MenuItem->fType; - } + { + lpmii->fType = MenuItem->fType; + } if(lpmii->fMask & MIIM_ID) - { - lpmii->wID = MenuItem->wID; - } + { + lpmii->wID = MenuItem->wID; + } if(lpmii->fMask & MIIM_STATE) - { - lpmii->fState = MenuItem->fState; - } + { + lpmii->fState = MenuItem->fState; + } if(lpmii->fMask & MIIM_SUBMENU) - { - lpmii->hSubMenu = MenuItem->hSubMenu; - } + { + lpmii->hSubMenu = MenuItem->hSubMenu; + } + if (0 != (lpmii->fMask & MIIM_STRING) || + 0 != (lpmii->fMask & MIIM_TYPE)) + { + Status = MmCopyFromCaller(&Text, lpmii->dwTypeData, sizeof(UNICODE_STRING)); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + Text.Length = min(Text.MaximumLength, MenuItem->Text.Length); + if (0 != Text.Length) + { + Status = MmCopyToCaller(Text.Buffer, MenuItem->Text.Buffer, Text.Length); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + } + Status = MmCopyToCaller(lpmii->dwTypeData, &Text, sizeof(UNICODE_STRING)); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + } + + if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize) + { + lpmii->Rect = MenuItem->Rect; + lpmii->XTab = MenuItem->XTab; + } return TRUE; } BOOL FASTCALL -IntSetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, LPCMENUITEMINFOW lpmii) +IntSetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, PROSMENUITEMINFO lpmii) { PUNICODE_STRING Source; UINT copylen = 0; @@ -710,43 +770,52 @@ IntSetMenuItemInfo(PMENU_OBJECT MenuObject, PMENU_ITEM MenuItem, LPCMENUITEMINFO { RtlInitUnicodeString(&MenuItem->Text, NULL); } + + if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize) + { + MenuItem->Rect = lpmii->Rect; + MenuItem->XTab = lpmii->XTab; + } return TRUE; } -BOOL FASTCALL +static BOOL FASTCALL IntInsertMenuItem(PMENU_OBJECT MenuObject, UINT uItem, BOOL fByPosition, - LPCMENUITEMINFOW lpmii) + PROSMENUITEMINFO ItemInfo) { int pos = (int)uItem; PMENU_ITEM MenuItem; - if(MenuObject->MenuItemCount >= MAX_MENU_ITEMS) - { - /* FIXME Set last error code? */ - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } + if (MAX_MENU_ITEMS <= MenuObject->MenuInfo.MenuItemCount) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } - if(fByPosition) - { - /* calculate position */ - if(pos > MenuObject->MenuItemCount) - pos = MenuObject->MenuItemCount; - } + if (fByPosition) + { + /* calculate position */ + if(MenuObject->MenuInfo.MenuItemCount < pos) + { + pos = MenuObject->MenuInfo.MenuItemCount; + } + } else - { - pos = IntGetMenuItemByFlag(MenuObject, uItem, MF_BYCOMMAND, NULL, NULL); - } - if(pos < -1) pos = -1; + { + pos = IntGetMenuItemByFlag(MenuObject, uItem, MF_BYCOMMAND, NULL, NULL); + } + if (pos < -1) + { + pos = -1; + } MenuItem = ExAllocatePool(PagedPool, sizeof(MENU_ITEM)); - if(!MenuItem) - { - /* FIXME Set last error code? */ - SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); - return FALSE; - } + if (NULL == MenuItem) + { + SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } MenuItem->fType = MFT_STRING; MenuItem->fState = MFS_ENABLED | MFS_UNCHECKED; @@ -758,11 +827,11 @@ IntInsertMenuItem(PMENU_OBJECT MenuObject, UINT uItem, BOOL fByPosition, RtlInitUnicodeString(&MenuItem->Text, NULL); MenuItem->hbmpItem = (HBITMAP)0; - if(!IntSetMenuItemInfo(MenuObject, MenuItem, lpmii)) - { - ExFreePool(MenuItem); - return FALSE; - } + if (! IntSetMenuItemInfo(MenuObject, MenuItem, ItemInfo)) + { + ExFreePool(MenuItem); + return FALSE; + } pos = IntInsertMenuItemToList(MenuObject, MenuItem, pos); @@ -1054,7 +1123,7 @@ IntInitTracking(PWINDOW_OBJECT WindowObject, PMENU_OBJECT MenuObject, BOOL Popup /* FIXME - send WM_SETCURSOR message */ if(!(Flags & TPM_NONOTIFY)) - IntSendMessage(WindowObject->Self, WM_INITMENU, (WPARAM)MenuObject->Self, 0); + IntSendMessage(WindowObject->Self, WM_INITMENU, (WPARAM)MenuObject->MenuInfo.Self, 0); } VOID FASTCALL @@ -1164,7 +1233,7 @@ NtUserBuildMenuItemList( } else { - res = MenuObject->MenuItemCount; + res = MenuObject->MenuInfo.MenuItemCount; } IntReleaseMenuObject(MenuObject); @@ -1313,21 +1382,40 @@ NtUserInsertMenuItem( HMENU hMenu, UINT uItem, BOOL fByPosition, - LPCMENUITEMINFOW lpmii) + LPCMENUITEMINFOW UnsafeItemInfo) { - DWORD res = 0; + DWORD Res = 0; PMENU_OBJECT MenuObject; + NTSTATUS Status; + ROSMENUITEMINFO ItemInfo; + MenuObject = IntGetMenuObject(hMenu); if(!MenuObject) - { - SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); - return 0; - } + { + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); + return 0; + } + + Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, sizeof(MENUITEMINFOW)); + if (! NT_SUCCESS(Status)) + { + IntReleaseMenuObject(MenuObject); + SetLastNtError(Status); + return FALSE; + } + if (ItemInfo.cbSize != sizeof(MENUITEMINFOW)) + { + IntReleaseMenuObject(MenuObject); + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return FALSE; + } + ExAcquireFastMutexUnsafe(&MenuObject->MenuItemsLock); - res = IntInsertMenuItem(MenuObject, uItem, fByPosition, lpmii); + Res = IntInsertMenuItem(MenuObject, uItem, fByPosition, &ItemInfo); ExReleaseFastMutexUnsafe(&MenuObject->MenuItemsLock); IntReleaseMenuObject(MenuObject); - return res; + + return Res; } @@ -1458,36 +1546,64 @@ NtUserHiliteMenuItem( BOOL STDCALL NtUserMenuInfo( - HMENU hmenu, - LPMENUINFO lpmi, - BOOL fsog) + HMENU Menu, + PROSMENUINFO UnsafeMenuInfo, + BOOL SetOrGet) { - BOOL res = FALSE; + BOOL Res; PMENU_OBJECT MenuObject; - - if(lpmi->cbSize != sizeof(MENUINFO)) - { - /* FIXME - Set Last Error */ - return FALSE; - } - MenuObject = IntGetMenuObject(hmenu); - if(!MenuObject) - { - SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); - return FALSE; - } - if(fsog) - { - /* Set MenuInfo */ - res = IntSetMenuInfo(MenuObject, lpmi); - } + DWORD Size; + NTSTATUS Status; + ROSMENUINFO MenuInfo; + + Status = MmCopyFromCaller(&Size, &UnsafeMenuInfo->cbSize, sizeof(DWORD)); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + if(Size < sizeof(MENUINFO) || sizeof(ROSMENUINFO) < Size) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return FALSE; + } + Status = MmCopyFromCaller(&MenuInfo, UnsafeMenuInfo, Size); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + + MenuObject = IntGetMenuObject(Menu); + if (NULL == MenuObject) + { + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); + return FALSE; + } + + if(SetOrGet) + { + /* Set MenuInfo */ + Res = IntSetMenuInfo(MenuObject, &MenuInfo); + } else - { - /* Get MenuInfo */ - res = IntGetMenuInfo(MenuObject, lpmi); - } + { + /* Get MenuInfo */ + Res = IntGetMenuInfo(MenuObject, &MenuInfo); + if (Res) + { + Status = MmCopyToCaller(UnsafeMenuInfo, &MenuInfo, Size); + if (! NT_SUCCESS(Status)) + { + SetLastNtError(Status); + return FALSE; + } + } + } + IntReleaseMenuObject(MenuObject); - return res; + + return Res; } @@ -1496,50 +1612,50 @@ NtUserMenuInfo( */ int STDCALL NtUserMenuItemFromPoint( - HWND hWnd, - HMENU hMenu, + HWND Wnd, + HMENU Menu, DWORD X, DWORD Y) { PMENU_OBJECT MenuObject; - PWINDOW_OBJECT WindowObject; + PWINDOW_OBJECT WindowObject = NULL; PMENU_ITEM mi; int i; - MenuObject = IntGetMenuObject(hMenu); - if(!MenuObject) - { - SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); - return -1; - } - if(hWnd) - { - WindowObject = IntGetWindowObject(hWnd); - if(!WindowObject) + MenuObject = IntGetMenuObject(Menu); + if (NULL == MenuObject) { - SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE); + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); return -1; } - } + if (0 != (MenuObject->MenuInfo.Flags & MF_POPUP)) + { + WindowObject = IntGetWindowObject(MenuObject->MenuInfo.Wnd); + if (NULL == WindowObject) + { + SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE); + return -1; + } + X -= WindowObject->WindowRect.left; + Y -= WindowObject->WindowRect.top; + IntReleaseWindowObject(WindowObject); + } ExAcquireFastMutexUnsafe(&MenuObject->MenuItemsLock); mi = MenuObject->MenuItemList; - for(i = 0; mi; i++) - { - if(InRect(mi->Rect, X, Y)) + for (i = 0; NULL != mi; i++) { - break; + if (InRect(mi->Rect, X, Y)) + { + break; + } + mi = mi->Next; } - mi = mi->Next; - } ExReleaseFastMutexUnsafe(&MenuObject->MenuItemsLock); IntReleaseMenuObject(MenuObject); - if(hWnd) - IntReleaseWindowObject(WindowObject); - - return (mi ? i : -1); + return (mi ? i : NO_SELECTED_ITEM); } @@ -1549,49 +1665,76 @@ NtUserMenuItemFromPoint( BOOL STDCALL NtUserMenuItemInfo( - HMENU hMenu, - UINT uItem, - BOOL fByPosition, - LPMENUITEMINFOW lpmii, - BOOL fsog) + HMENU Menu, + UINT Item, + BOOL ByPosition, + PROSMENUITEMINFO UnsafeItemInfo, + BOOL SetOrGet) { PMENU_OBJECT MenuObject; PMENU_ITEM MenuItem; - MENUITEMINFOW Safemii; + ROSMENUITEMINFO ItemInfo; NTSTATUS Status; + UINT Size; + BOOL Ret; - MenuObject = IntGetMenuObject(hMenu); - if(!MenuObject) - { - SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); - return FALSE; - } - - if(fsog) - { - /* Set menu item info */ - } - else - { - if((IntGetMenuItemByFlag(MenuObject, uItem, (fByPosition ? MF_BYPOSITION : MF_BYCOMMAND), - &MenuItem, NULL) > -1) && - IntGetMenuItemInfo(MenuObject, MenuItem, &Safemii)) + MenuObject = IntGetMenuObject(Menu); + if (NULL == MenuObject) { - Status = MmCopyToCaller(lpmii, &Safemii, sizeof(MENUITEMINFOW)); - if(!NT_SUCCESS(Status)) - { - IntReleaseMenuObject(MenuObject); - SetLastNtError(Status); - return FALSE; - } - - IntReleaseMenuObject(MenuObject); - return TRUE; + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); + return FALSE; + } + Status = MmCopyFromCaller(&Size, &UnsafeItemInfo->cbSize, sizeof(UINT)); + if (! NT_SUCCESS(Status)) + { + IntReleaseMenuObject(MenuObject); + SetLastNtError(Status); + return FALSE; + } + if(sizeof(MENUITEMINFOW) != Size && sizeof(ROSMENUITEMINFO) != Size) + { + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return FALSE; + } + Status = MmCopyFromCaller(&ItemInfo, UnsafeItemInfo, Size); + if (! NT_SUCCESS(Status)) + { + IntReleaseMenuObject(MenuObject); + SetLastNtError(Status); + return FALSE; + } + + if (IntGetMenuItemByFlag(MenuObject, Item, + (ByPosition ? MF_BYPOSITION : MF_BYCOMMAND), + &MenuItem, NULL) < 0) + { + IntReleaseMenuObject(MenuObject); + SetLastWin32Error(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (SetOrGet) + { + Ret = IntSetMenuItemInfo(MenuObject, MenuItem, &ItemInfo); + } + else + { + Ret = IntGetMenuItemInfo(MenuObject, MenuItem, &ItemInfo); + if (Ret) + { + Status = MmCopyToCaller(UnsafeItemInfo, &ItemInfo, Size); + if (! NT_SUCCESS(Status)) + { + IntReleaseMenuObject(MenuObject); + SetLastNtError(Status); + return FALSE; + } + } } - } IntReleaseMenuObject(MenuObject); - return FALSE; + + return Ret; } diff --git a/reactos/subsys/win32k/ntuser/misc.c b/reactos/subsys/win32k/ntuser/misc.c index 20aa321087c..35325d45513 100644 --- a/reactos/subsys/win32k/ntuser/misc.c +++ b/reactos/subsys/win32k/ntuser/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.48 2004/01/31 14:57:58 rcampbell Exp $ +/* $Id: misc.c,v 1.49 2004/02/15 07:39:12 gvg Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -205,11 +205,11 @@ NtUserCallTwoParam( if(Param2 > 0) { - Ret = (MenuObject->Height == (int)Param2); - MenuObject->Height = (int)Param2; + Ret = (MenuObject->MenuInfo.Height == (int)Param2); + MenuObject->MenuInfo.Height = (int)Param2; } else - Ret = (DWORD)MenuObject->Height; + Ret = (DWORD)MenuObject->MenuInfo.Height; IntReleaseMenuObject(MenuObject); return Ret; } diff --git a/reactos/subsys/win32k/ntuser/window.c b/reactos/subsys/win32k/ntuser/window.c index 6aff80e67cd..c2daea27b36 100644 --- a/reactos/subsys/win32k/ntuser/window.c +++ b/reactos/subsys/win32k/ntuser/window.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* $Id: window.c,v 1.182 2004/02/08 10:53:17 navaraf Exp $ +/* $Id: window.c,v 1.183 2004/02/15 07:39:12 gvg Exp $ * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel @@ -432,6 +432,70 @@ static LRESULT IntDestroyWindow(PWINDOW_OBJECT Window, return 0; } +static BOOL FASTCALL +IntSetMenu( + PWINDOW_OBJECT WindowObject, + HMENU Menu, + BOOL *Changed) +{ + PMENU_OBJECT OldMenuObject, NewMenuObject; + + *Changed = (WindowObject->IDMenu != (UINT) Menu); + if (! *Changed) + { + return TRUE; + } + + if (0 != WindowObject->IDMenu) + { + OldMenuObject = IntGetMenuObject((HMENU) WindowObject->IDMenu); + ASSERT(NULL == OldMenuObject || OldMenuObject->MenuInfo.Wnd == WindowObject->Self); + } + else + { + OldMenuObject = NULL; + } + + if (NULL != Menu) + { + NewMenuObject = IntGetMenuObject(Menu); + if (NULL == NewMenuObject) + { + if (NULL != OldMenuObject) + { + IntReleaseMenuObject(OldMenuObject); + } + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); + return FALSE; + } + if (NULL != NewMenuObject->MenuInfo.Wnd) + { + /* Can't use the same menu for two windows */ + if (NULL != OldMenuObject) + { + IntReleaseMenuObject(OldMenuObject); + } + SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); + return FALSE; + } + + } + + WindowObject->IDMenu = (UINT) Menu; + if (NULL != NewMenuObject) + { + NewMenuObject->MenuInfo.Wnd = WindowObject->Self; + IntReleaseMenuObject(NewMenuObject); + } + if (NULL != OldMenuObject) + { + OldMenuObject->MenuInfo.Wnd = NULL; + IntReleaseMenuObject(OldMenuObject); + } + + return TRUE; +} + /* INTERNAL ******************************************************************/ @@ -557,8 +621,8 @@ IntGetSystemMenu(PWINDOW_OBJECT WindowObject, BOOL bRevert, BOOL RetMenu) NewMenuObject = IntCloneMenu(MenuObject); if(NewMenuObject) { - WindowObject->SystemMenu = NewMenuObject->Self; - NewMenuObject->IsSystemMenu = TRUE; + WindowObject->SystemMenu = NewMenuObject->MenuInfo.Self; + NewMenuObject->MenuInfo.Flags |= MF_SYSMENU; ret = NewMenuObject; //IntReleaseMenuObject(NewMenuObject); } @@ -576,8 +640,8 @@ IntGetSystemMenu(PWINDOW_OBJECT WindowObject, BOOL bRevert, BOOL RetMenu) NewMenuObject = IntCloneMenu(MenuObject); if(NewMenuObject) { - WindowObject->SystemMenu = NewMenuObject->Self; - NewMenuObject->IsSystemMenu = TRUE; + WindowObject->SystemMenu = NewMenuObject->MenuInfo.Self; + NewMenuObject->MenuInfo.Flags |= MF_SYSMENU; ret = NewMenuObject; //IntReleaseMenuObject(NewMenuObject); } @@ -726,6 +790,7 @@ IntSetParent(PWINDOW_OBJECT Wnd, PWINDOW_OBJECT WndNewParent) PWINDOW_OBJECT WndOldParent; HWND hWnd, hWndNewParent, hWndOldParent; BOOL WasVisible; + BOOL MenuChanged; hWnd = Wnd->Self; hWndNewParent = WndNewParent->Self; @@ -757,7 +822,7 @@ IntSetParent(PWINDOW_OBJECT Wnd, PWINDOW_OBJECT WndNewParent) if (!(Wnd->Style & WS_CHILD)) { //if ( Wnd->Menu ) DestroyMenu ( Wnd->menu ); - Wnd->IDMenu = 0; + IntSetMenu(Wnd, NULL, &MenuChanged); } } } @@ -791,7 +856,7 @@ IntSetSystemMenu(PWINDOW_OBJECT WindowObject, PMENU_OBJECT MenuObject) OldMenuObject = IntGetMenuObject(WindowObject->SystemMenu); if(OldMenuObject) { - OldMenuObject->IsSystemMenu = FALSE; + OldMenuObject->MenuInfo.Flags &= ~ MF_SYSMENU; IntReleaseMenuObject(OldMenuObject); } } @@ -799,8 +864,8 @@ IntSetSystemMenu(PWINDOW_OBJECT WindowObject, PMENU_OBJECT MenuObject) if(MenuObject) { /* FIXME check window style, propably return FALSE ? */ - WindowObject->SystemMenu = MenuObject->Self; - MenuObject->IsSystemMenu = TRUE; + WindowObject->SystemMenu = MenuObject->MenuInfo.Self; + MenuObject->MenuInfo.Flags |= MF_SYSMENU; } else WindowObject->SystemMenu = (HMENU)0; @@ -1096,6 +1161,7 @@ NtUserCreateWindowEx(DWORD dwExStyle, CREATESTRUCTW Cs; CBT_CREATEWNDW CbtCreate; LRESULT Result; + BOOL MenuChanged; DPRINT("NtUserCreateWindowEx(): (%d,%d-%d,%d)\n", x, y, nWidth, nHeight); @@ -1211,9 +1277,10 @@ NtUserCreateWindowEx(DWORD dwExStyle, WindowObject->SystemMenu = (HMENU)0; WindowObject->ContextHelpId = 0; - WindowObject->IDMenu = (UINT)hMenu; + WindowObject->IDMenu = 0; WindowObject->Instance = hInstance; WindowObject->Self = Handle; + IntSetMenu(WindowObject, hMenu, &MenuChanged); WindowObject->MessageQueue = PsGetWin32Thread()->MessageQueue; WindowObject->Parent = ParentWindow; WindowObject->Owner = IntGetWindowObject(OwnerWindowHandle); @@ -1275,7 +1342,7 @@ NtUserCreateWindowEx(DWORD dwExStyle, SystemMenu = IntGetSystemMenu(WindowObject, TRUE, TRUE); if(SystemMenu) { - WindowObject->SystemMenu = SystemMenu->Self; + WindowObject->SystemMenu = SystemMenu->MenuInfo.Self; IntReleaseMenuObject(SystemMenu); } } @@ -3038,53 +3105,35 @@ NtUserSetLogonNotifyWindow(DWORD Unknown0) */ BOOL STDCALL NtUserSetMenu( - HWND hWnd, - HMENU hMenu, - BOOL bRepaint) + HWND Wnd, + HMENU Menu, + BOOL Repaint) { - PWINDOW_OBJECT WindowObject; - PMENU_OBJECT MenuObject; - BOOL Changed = FALSE; + PWINDOW_OBJECT WindowObject; + BOOL Changed; - WindowObject = IntGetWindowObject((HWND)hWnd); - if (!WindowObject) - { + WindowObject = IntGetWindowObject((HWND) Wnd); + if (NULL == WindowObject) + { SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE); return FALSE; - } + } - if (hMenu) - { - /* assign new menu handle */ - MenuObject = IntGetMenuObject((HWND)hMenu); - if (!MenuObject) - { - IntReleaseWindowObject(WindowObject); - SetLastWin32Error(ERROR_INVALID_MENU_HANDLE); - return FALSE; - } - - Changed = (WindowObject->IDMenu != (UINT)hMenu); - WindowObject->IDMenu = (UINT)hMenu; - - IntReleaseMenuObject(MenuObject); - } - else - { - /* remove the menu handle */ - Changed = (WindowObject->IDMenu != 0); - WindowObject->IDMenu = 0; - } + if (! IntSetMenu(WindowObject, Menu, &Changed)) + { + IntReleaseWindowObject(WindowObject); + return FALSE; + } - IntReleaseWindowObject(WindowObject); + IntReleaseWindowObject(WindowObject); - if (Changed && bRepaint) - { - WinPosSetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | + if (Changed && Repaint) + { + WinPosSetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); - } + } - return TRUE; + return TRUE; }