3816 lines
116 KiB
C++
3816 lines
116 KiB
C++
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// MENU.CPP
|
||
|
//
|
||
|
// Menu class, for system menus and app menus.
|
||
|
//
|
||
|
// There are four classes here.
|
||
|
// CMenu is the class that knows how to deal with menu bar objects. These
|
||
|
// have children that are CMenuItem objects, or just children (rare -
|
||
|
// this is when you have a command right on the menu bar).
|
||
|
// CMenuItem is something that when you click on it opens a popup.
|
||
|
// It has 1 child that is a CMenuPopupFrame.
|
||
|
// CMenuPopupFrame is the HWND that pops up when you click on a CMenuItem. It
|
||
|
// has 1 child, a CMenuPopup.
|
||
|
// CMenuPopup objects represent the client area of a CMenuPopupFrame HWND.
|
||
|
// It has children that are menu items (little m, little i), separators, and
|
||
|
// CMenuItems (when you have cascading menus).
|
||
|
//
|
||
|
// Issues that came up during design/implementation:
|
||
|
// (1) How do we select/focus menu items while in menu mode?
|
||
|
// (2) How do we choose an item (default action)?
|
||
|
// For menu bars, we use SendInput to send Alt+Shortcut key to
|
||
|
// open or execute an item or command. Send just Alt to close
|
||
|
// an item that is already open.
|
||
|
// (3) How do we handle popup menus?
|
||
|
// As discussed above, we treat them very strangely. There are ways
|
||
|
// to get the children in a popup whether it is visible or not.
|
||
|
// (4) What about "system menu" popups on tray?
|
||
|
//
|
||
|
// (5) In general, what about "context menus"? This may need to be
|
||
|
// exposed by the app itself. We can't do everything!
|
||
|
//
|
||
|
// History:
|
||
|
// written by Laura Butler, early 1996
|
||
|
// complete re-write by Steve Donie, August 1996-January 1997
|
||
|
// doDefaultAction changed to use keypresses rather than mouse clicks 3-97
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
#include "oleacc_p.h"
|
||
|
#include "default.h"
|
||
|
#include "classmap.h"
|
||
|
#include "ctors.h"
|
||
|
#include "window.h"
|
||
|
#include "client.h"
|
||
|
#include "menu.h"
|
||
|
|
||
|
#include "propmgr_util.h"
|
||
|
|
||
|
#define MI_NONE -1
|
||
|
|
||
|
#ifndef MFS_HOTTRACK
|
||
|
#define MFS_HOTTRACK 0x00000100L
|
||
|
#endif // !MFS_HOTTRACK
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// prototypes for local functions
|
||
|
// --------------------------------------------------------------------------
|
||
|
HWND GetSubMenuWindow (HMENU hSubMenuToFind);
|
||
|
long FindItemIDThatOwnsThisMenu (HMENU hMenuOwned,HWND* phwndOwner,
|
||
|
BOOL* pfPopup,BOOL* pfSysMenu);
|
||
|
STDAPI WindowFromAccessibleObjectEx(IAccessible* pacc, HWND* phwnd);
|
||
|
|
||
|
BOOL MyGetMenuString( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, long id, LPTSTR lpszBuf, UINT cchMax, BOOL fAllowGenerated );
|
||
|
|
||
|
BOOL TryMSAAMenuHack( IAccessible * pTheObj, HWND hWnd, DWORD_PTR dwItemData, LPTSTR lpszBuf, UINT cchMax );
|
||
|
BOOL GetShellOwnerDrawMenu( HWND hwnd, DWORD_PTR dwItemData, LPTSTR lpszBuf, UINT cchMax );
|
||
|
|
||
|
UINT GetMDIButtonIndex( HMENU hMenu, DWORD idPos );
|
||
|
BOOL GetMDIMenuDescriptionString( HMENU hMenu, DWORD idPos, BSTR * pbstr );
|
||
|
BOOL GetMDIMenuString( HWND hwnd, HMENU hMenu, DWORD idPos, LPTSTR lpszBuf, UINT cchMax );
|
||
|
|
||
|
HMENU MyGetSystemMenu( HWND hwnd );
|
||
|
|
||
|
HRESULT GetMenuItemName( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id, BSTR * pszName );
|
||
|
TCHAR GetMenuItemHotkey( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id, DWORD fOptions );
|
||
|
HRESULT GetMenuItemShortcut( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id,
|
||
|
BOOL fIsMenuBar, BSTR * pszShortcut );
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
GMIH_ALLOW_INITIAL = 0x01,
|
||
|
GMIH_ALLOW_SYS_SPACE = 0x02
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateSysMenuBarObject()
|
||
|
//
|
||
|
// EXTERNAL for CreateStdAcessibleObject
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hwnd IN window handle of the window that owns this menu
|
||
|
// idChildCur IN id of the current child. Will be 0 when creating the
|
||
|
// system menu bar and application menu bar. Will be the
|
||
|
// id of a child when calling CMenu::Clone()
|
||
|
// riid IN the id of the interface asked for
|
||
|
// ppvMenu OUT ppvMenu holds the indirect pointer to the menu
|
||
|
// object.
|
||
|
//
|
||
|
// Return Value:
|
||
|
// S_OK if the interface is supported, E_NOINTERFACE if not,
|
||
|
// E_OUTOFMEMORY if not enough memory to create the menu object,
|
||
|
// E_FAIL if the hwnd is invalid.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateSysMenuBarObject(HWND hwnd, long idObject, REFIID riid,
|
||
|
void** ppvMenu)
|
||
|
{
|
||
|
UNUSED(idObject);
|
||
|
|
||
|
if (!IsWindow(hwnd))
|
||
|
return(E_FAIL);
|
||
|
|
||
|
return(CreateMenuBar(hwnd, TRUE, 0, riid, ppvMenu));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateMenuBarObject()
|
||
|
//
|
||
|
// EXTERNAL for CreateStdAcessibleObject
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hwnd IN window handle of the window that owns this menu
|
||
|
// idChildCur IN id of the current child. Will be 0 when creating the
|
||
|
// system menu bar and application menu bar. Will be the
|
||
|
// id of a child when calling CMenu::Clone()
|
||
|
// riid IN the id of the interface asked for (like IAccessible,
|
||
|
// IEnumVARIANT,IDispatch...)
|
||
|
// ppvMenu OUT ppvMenu is where QueryInterface will return the
|
||
|
// indirect pointer to the menu object (caller casts
|
||
|
// this to be a pointer to the interface they asked for)
|
||
|
//
|
||
|
// Return Value:
|
||
|
// S_OK if the interface is supported, E_NOINTERFACE if not,
|
||
|
// E_OUTOFMEMORY if not enough memory to create the menu object,
|
||
|
// E_FAIL if the hwnd is invalid.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateMenuBarObject(HWND hwnd, long idObject, REFIID riid, void** ppvMenu)
|
||
|
{
|
||
|
UNUSED(idObject);
|
||
|
|
||
|
if (!IsWindow(hwnd))
|
||
|
return(E_FAIL);
|
||
|
|
||
|
return(CreateMenuBar(hwnd, FALSE, 0, riid, ppvMenu));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateMenuBar()
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hwnd IN window handle of the window that owns this menu
|
||
|
// fSysMenu IN true if this is a system menu, false if app menu
|
||
|
// idChildCur IN id of the current child. Will be 0 when creating the
|
||
|
// system menu bar and application menu bar. Will be the
|
||
|
// id of a child when calling CMenu::Clone()
|
||
|
// riid IN the id of the interface asked for
|
||
|
// ppvMenu OUT ppvMenu is where QueryInterface will return the
|
||
|
// indirect pointer to the menu object
|
||
|
//
|
||
|
// Return Value:
|
||
|
// S_OK if the interface is supported, E_NOINTERFACE if not,
|
||
|
// E_OUTOFMEMORY if not enough memory to create the menu object.
|
||
|
//
|
||
|
// Called By:
|
||
|
// CreateMenuBarObject and CMenu::Clone
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateMenuBar(HWND hwnd, BOOL fSysMenu, long idChildCur,
|
||
|
REFIID riid, void** ppvMenu)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CMenu* pmenu;
|
||
|
|
||
|
InitPv(ppvMenu);
|
||
|
|
||
|
pmenu = new CMenu(hwnd, fSysMenu, idChildCur);
|
||
|
if (!pmenu)
|
||
|
return(E_OUTOFMEMORY);
|
||
|
|
||
|
hr = pmenu->QueryInterface(riid, ppvMenu);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
delete pmenu;
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::CMenu()
|
||
|
//
|
||
|
// Constructor for the CMenu class. Initializes the member variables with
|
||
|
// the passed in parameters. It is only called by CreateMenuBar.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenu::CMenu(HWND hwnd, BOOL fSysMenu, long idChildCur)
|
||
|
: CAccessible( CLASS_MenuObject )
|
||
|
{
|
||
|
m_hwnd = hwnd;
|
||
|
m_fSysMenu = fSysMenu;
|
||
|
m_idChildCur = idChildCur;
|
||
|
m_hMenu = NULL;
|
||
|
// m_hMenu is filled in by SetupChildren()
|
||
|
// m_cChildren is filled in by SetupChildren()
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::SetupChildren()
|
||
|
//
|
||
|
// This uses the object's window handle to get the handle to the appropriate
|
||
|
// menu (hmenu type menu handle) and the count of the children in that menu.
|
||
|
// It uses GetMenuBarInfo, a private function in USER, to do this.
|
||
|
// These values are kept as member variables of the CMenu object.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
void CMenu::SetupChildren(void)
|
||
|
{
|
||
|
MENUBARINFO mbi;
|
||
|
|
||
|
if (!MyGetMenuBarInfo(m_hwnd, (m_fSysMenu ? OBJID_SYSMENU : OBJID_MENU),
|
||
|
0, &mbi))
|
||
|
{
|
||
|
m_hMenu = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_hMenu = mbi.hMenu;
|
||
|
}
|
||
|
|
||
|
if (!m_hMenu)
|
||
|
m_cChildren = 0;
|
||
|
else
|
||
|
{
|
||
|
m_cChildren = GetMenuItemCount(m_hMenu);
|
||
|
|
||
|
if( m_cChildren == -1 )
|
||
|
{
|
||
|
// Paranoia in case we get an invalid HMENU
|
||
|
m_cChildren = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accChild()
|
||
|
//
|
||
|
// What we want this do do is return (in ppdisp) an IDispatch pointer to
|
||
|
// the child specified by varChild. The children of a CMenu are either
|
||
|
// menu commands (rare to have a command on the menu bar) and CMenuItems.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accChild(VARIANT varChild, IDispatch** ppdisp)
|
||
|
{
|
||
|
HMENU hSubMenu;
|
||
|
|
||
|
InitPv(ppdisp);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return (E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
Assert (m_hMenu);
|
||
|
|
||
|
hSubMenu = GetSubMenu(m_hMenu, varChild.lVal-1);
|
||
|
|
||
|
if (hSubMenu == NULL)
|
||
|
{
|
||
|
// This returns false - for commands on the menu bar, we do not create a child
|
||
|
// object - the parent is able to answer all the questions.
|
||
|
return (S_FALSE);
|
||
|
}
|
||
|
|
||
|
return(CreateMenuItem((IAccessible*)this, m_hwnd, m_hMenu,hSubMenu,
|
||
|
varChild.lVal, 0, TRUE, IID_IDispatch, (void**)ppdisp));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accName()
|
||
|
//
|
||
|
// Pass in a VARIANT with type VT_I4 and lVal equal to the 1-based position
|
||
|
// of the item you want the name for. Pass in a pointer to a string and the
|
||
|
// string will be filled with the name.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accName(VARIANT varChild, BSTR* pszName)
|
||
|
{
|
||
|
InitPv(pszName);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal != CHILDID_SELF)
|
||
|
{
|
||
|
return GetMenuItemName( this, m_hwnd, m_hMenu, varChild.lVal, pszName );
|
||
|
}
|
||
|
|
||
|
if (m_fSysMenu)
|
||
|
return HrCreateString(STR_SYSMENU_NAME, pszName); // in English = "System"
|
||
|
else
|
||
|
return HrCreateString(STR_MENUBAR_NAME, pszName); // in English, this is "Application"
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accDescription()
|
||
|
//
|
||
|
// get a string with the description of this menu item. For CMenu, this
|
||
|
// is something like "contains commands to manipulate the window" or
|
||
|
// "Contains commands to manipulate the current view or document"
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accDescription(VARIANT varChild, BSTR* pszDesc)
|
||
|
{
|
||
|
|
||
|
InitPv(pszDesc);
|
||
|
|
||
|
if (! ValidateChild(&varChild))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
// Check if they are asking about the menu bar itself or a child.
|
||
|
// If asking about a child, return S_FALSE, because we don't have
|
||
|
// descriptions for items, just the system and app menu bars.
|
||
|
//
|
||
|
if (varChild.lVal != CHILDID_SELF)
|
||
|
{
|
||
|
if( GetMDIMenuDescriptionString( m_hMenu, varChild.lVal - 1, pszDesc ) )
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
else if (m_fSysMenu)
|
||
|
{
|
||
|
return HrCreateString(STR_SYSMENUBAR_DESCRIPTION, pszDesc);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return HrCreateString(STR_MENUBAR_DESCRIPTION, pszDesc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accRole()
|
||
|
//
|
||
|
// get the role - this is either menu item or menu bar
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accRole(VARIANT varChild, VARIANT* pvarRole)
|
||
|
{
|
||
|
InitPvar(pvarRole);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
pvarRole->vt = VT_I4;
|
||
|
|
||
|
if (varChild.lVal != CHILDID_SELF)
|
||
|
{
|
||
|
if( GetMDIButtonIndex( m_hMenu, varChild.lVal - 1 ) != 0 )
|
||
|
{
|
||
|
// Special case for MDI child buttons - they are actually implemented
|
||
|
// as menu items, but appear as buttons.
|
||
|
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUITEM;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUBAR;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accState()
|
||
|
//
|
||
|
// get the state of the child specified. returned in a variant VT_I4
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accState(VARIANT varChild, VARIANT* pvarState)
|
||
|
{
|
||
|
MENUBARINFO mbi;
|
||
|
|
||
|
InitPvar(pvarState);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
pvarState->vt = VT_I4;
|
||
|
pvarState->lVal = 0;
|
||
|
|
||
|
if (!m_hMenu || !MyGetMenuBarInfo(m_hwnd, (m_fSysMenu ? OBJID_SYSMENU : OBJID_MENU),
|
||
|
varChild.lVal, &mbi))
|
||
|
{
|
||
|
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
|
||
|
}
|
||
|
else if( varChild.lVal && GetMDIButtonIndex( m_hMenu, varChild.lVal - 1 ) != 0 )
|
||
|
{
|
||
|
// For MDI button elements, just leave the state as normal, to be consistent
|
||
|
// with the top-level restore/close/minimize buttons.
|
||
|
|
||
|
// Do nothing here.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Non-MDI-button menu items, or CHILDID_SELF...
|
||
|
|
||
|
// smd 1-29-97 - change from OFFSCREEN to INVISIBLE
|
||
|
if (IsRectEmpty(&mbi.rcBar))
|
||
|
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
|
||
|
|
||
|
if (GetForegroundWindow() == m_hwnd)
|
||
|
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
||
|
|
||
|
if (mbi.fFocused)
|
||
|
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
||
|
|
||
|
if (varChild.lVal)
|
||
|
{
|
||
|
MENUITEMINFO mi;
|
||
|
|
||
|
//
|
||
|
// Get menu item flags. NOTE: Can't use GetMenuState(). It whacks
|
||
|
// random stuff in for hierarchicals.
|
||
|
//
|
||
|
mi.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mi.fMask = MIIM_STATE | MIIM_SUBMENU;
|
||
|
|
||
|
if (!GetMenuItemInfo(m_hMenu, varChild.lVal-1, TRUE, &mi))
|
||
|
{
|
||
|
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
if (mi.fState & MFS_GRAYED)
|
||
|
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
|
||
|
|
||
|
if (mi.fState & MFS_CHECKED)
|
||
|
pvarState->lVal |= STATE_SYSTEM_CHECKED;
|
||
|
|
||
|
if (mi.fState & MFS_DEFAULT)
|
||
|
pvarState->lVal |= STATE_SYSTEM_DEFAULT;
|
||
|
|
||
|
if (mbi.fFocused)
|
||
|
{
|
||
|
pvarState->lVal |= STATE_SYSTEM_HOTTRACKED;
|
||
|
if (mi.fState & MFS_HILITE)
|
||
|
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
||
|
}
|
||
|
|
||
|
if (mi.hSubMenu)
|
||
|
pvarState->lVal |= STATE_SYSTEM_HASPOPUP;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accKeyboardShortcut()
|
||
|
//
|
||
|
// returns a string with the menu shortcut to the child asked for
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszShortcut)
|
||
|
{
|
||
|
InitPv(pszShortcut);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
if (!m_hMenu)
|
||
|
return S_FALSE;
|
||
|
|
||
|
if (m_fSysMenu)
|
||
|
{
|
||
|
// Alt+Space/Hyphen is the system menu...
|
||
|
TCHAR szFormat[16];
|
||
|
LoadString(hinstResDll, STR_MENU_SHORTCUT_FORMAT, szFormat, ARRAYSIZE(szFormat));
|
||
|
|
||
|
TCHAR szKey[16];
|
||
|
LoadString(hinstResDll, ((GetWindowLong(m_hwnd, GWL_STYLE) & WS_CHILD) ?
|
||
|
STR_CHILDSYSMENU_KEY : STR_SYSMENU_KEY), szKey, ARRAYSIZE(szKey));
|
||
|
|
||
|
TCHAR szHotKey[32];
|
||
|
szHotKey[ 0 ] = '\0';
|
||
|
wsprintf(szHotKey, szFormat, szKey);
|
||
|
|
||
|
if (*szHotKey)
|
||
|
{
|
||
|
*pszShortcut = TCharSysAllocString(szHotKey);
|
||
|
if (! *pszShortcut)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// "Alt" is the menu bar
|
||
|
return HrCreateString(STR_MENU_SHORTCUT, pszShortcut);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Get menu item shortcut - TRUE means use "Alt+" format.
|
||
|
return GetMenuItemShortcut( this, m_hwnd, m_hMenu, varChild.lVal, TRUE, pszShortcut );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accFocus()
|
||
|
//
|
||
|
// This fills in pvarFocus with the ID of the child that has the focus.
|
||
|
// So when say you just hit "Alt" (File is now highlighted) and then call
|
||
|
// get_accFocus(), pvarFocus will have VT_I4 and lVal = 1.
|
||
|
//
|
||
|
// If we are not in menu mode, then we certainly don't have the focus.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accFocus(VARIANT* pvarFocus)
|
||
|
{
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
MENUITEMINFO mii;
|
||
|
int i;
|
||
|
|
||
|
// set it to empty
|
||
|
InitPvar(pvarFocus);
|
||
|
|
||
|
//
|
||
|
// Are we in menu mode? If not, nothing.
|
||
|
//
|
||
|
if (!MyGetGUIThreadInfo (NULL,&GuiThreadInfo))
|
||
|
return(S_FALSE);
|
||
|
|
||
|
if (GuiThreadInfo.flags & GUI_INMENUMODE)
|
||
|
{
|
||
|
// do I have to loop through all of them to see which
|
||
|
// one is hilited?? Looks like it...
|
||
|
mii.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mii.fMask = MIIM_STATE;
|
||
|
|
||
|
SetupChildren();
|
||
|
for (i=0;i < m_cChildren;i++)
|
||
|
{
|
||
|
GetMenuItemInfo (m_hMenu,i,TRUE,&mii);
|
||
|
if (mii.fState & MFS_HILITE)
|
||
|
{
|
||
|
pvarFocus->vt = VT_I4;
|
||
|
pvarFocus->lVal = i+1;
|
||
|
return (S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// I don't think this should happen
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::get_accDefaultAction()
|
||
|
//
|
||
|
// Menu bars have no defaults. However, items do. Hierarchical items
|
||
|
// drop down/pop up their hierarchical. Non-hierarchical items execute
|
||
|
// their command.
|
||
|
//
|
||
|
// doDefaultAction follows from this. It has to do whatever getDefaultAction
|
||
|
// says it is going to do. We use keystrokes to do this for menu bars.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::get_accDefaultAction(VARIANT varChild, BSTR* pszDefA)
|
||
|
{
|
||
|
GUITHREADINFO gui;
|
||
|
HMENU hSubMenu;
|
||
|
|
||
|
InitPv(pszDefA);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
|
||
|
if( GetMDIButtonIndex( m_hMenu, varChild.lVal - 1 ) != 0 )
|
||
|
{
|
||
|
// Special case for MDI child buttons - they are actually implemented
|
||
|
// as menu items, but appear as buttons.
|
||
|
return HrCreateString(STR_BUTTON_PUSH, pszDefA);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Only if this window is active can we do the default.
|
||
|
// There is a slight danger here that an Always On Top window
|
||
|
// could be covering us, but this is small.
|
||
|
//
|
||
|
if (!MyGetGUIThreadInfo(0, &gui))
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
if (m_hwnd != gui.hwndActive)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
varChild.lVal--;
|
||
|
|
||
|
// Is this item enabled?
|
||
|
if (GetMenuState(m_hMenu, varChild.lVal, MF_BYPOSITION) & MFS_GRAYED)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
// Now check if this item has a submenu that is displayed.
|
||
|
// If there is, the action is hide, if not, the action is show.
|
||
|
// If it doesn't have a submenu, the action is execute.
|
||
|
if (hSubMenu = GetSubMenu(m_hMenu, varChild.lVal))
|
||
|
{
|
||
|
if (GetSubMenuWindow(hSubMenu))
|
||
|
return(HrCreateString(STR_DROPDOWN_HIDE, pszDefA));
|
||
|
else
|
||
|
return(HrCreateString(STR_DROPDOWN_SHOW, pszDefA));
|
||
|
}
|
||
|
else
|
||
|
return(HrCreateString(STR_EXECUTE, pszDefA));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::accSelect()
|
||
|
//
|
||
|
// We only accept TAKE_FOCUS. What I wanted this to do is to just put the
|
||
|
// app into menu mode (if it isn't already - more on this later) and then
|
||
|
// select the item specified - don't open it or anything, just select it.
|
||
|
//
|
||
|
// But that was a pain in the butt, so no I just use doDefaultAction to
|
||
|
// do the work. Maybe I'll fix it for 1.1
|
||
|
//
|
||
|
// If we are already in menu mode, and a popup is open, then we should
|
||
|
// close the popup(s) and select the item. If in menu mode and no popups
|
||
|
// are up, just select the item.
|
||
|
//
|
||
|
// If the app is just setting focus to the menu bar itself, and not already
|
||
|
// in menu mode, just put us into menu mode (automatically selects first
|
||
|
// item). If we are already in menu mode, do nothing.
|
||
|
//
|
||
|
// I want to try to do all this without generating a whole mess of extra
|
||
|
// events!
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::accSelect(long flagsSel, VARIANT varChild)
|
||
|
{
|
||
|
LPARAM lParam;
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
|
||
|
if (!ValidateChild(&varChild) || !ValidateSelFlags(flagsSel))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (flagsSel != SELFLAG_TAKEFOCUS)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
// if this window is not the active window, fail.
|
||
|
MyGetGUIThreadInfo (NULL,&GuiThreadInfo);
|
||
|
if (GuiThreadInfo.hwndActive != m_hwnd)
|
||
|
return (E_NOT_APPLICABLE);
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (!m_hMenu)
|
||
|
{
|
||
|
//DBPRINTF (TEXT("null hmenu at 1\r\n"));
|
||
|
Assert (m_hMenu);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
if (!m_fSysMenu)
|
||
|
lParam = NULL;
|
||
|
else if (GetWindowLong(m_hwnd, GWL_STYLE) & WS_CHILD)
|
||
|
lParam = MAKELONG('-', 0);
|
||
|
else
|
||
|
lParam = MAKELONG(' ', 0);
|
||
|
|
||
|
PostMessage(m_hwnd, WM_SYSCOMMAND, SC_KEYMENU, lParam);
|
||
|
return (S_OK);
|
||
|
}
|
||
|
else if (GetSubMenu(m_hMenu, varChild.lVal-1))
|
||
|
{
|
||
|
// for version 1.0, I'll just do this. Safe, even though it's not 100%
|
||
|
// what I want it to do.
|
||
|
return (accDoDefaultAction (varChild));
|
||
|
}
|
||
|
|
||
|
return (E_FAIL);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::accLocation()
|
||
|
//
|
||
|
// get the location of the child. left,top,width,height
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
|
||
|
long* pcyHeight, VARIANT varChild)
|
||
|
{
|
||
|
MENUBARINFO mbi;
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
|
||
|
|
||
|
if (!MyGetMenuBarInfo(m_hwnd, (m_fSysMenu ? OBJID_SYSMENU : OBJID_MENU),
|
||
|
varChild.lVal, &mbi))
|
||
|
return(S_FALSE);
|
||
|
|
||
|
*pcxWidth = mbi.rcBar.right - mbi.rcBar.left;
|
||
|
*pcyHeight = mbi.rcBar.bottom - mbi.rcBar.top;
|
||
|
|
||
|
*pxLeft = mbi.rcBar.left;
|
||
|
*pyTop = mbi.rcBar.top;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::accHitTest()
|
||
|
//
|
||
|
// if the point is in a menu bar, return the child the point is over
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::accHitTest(long x, long y, VARIANT* pvarHit)
|
||
|
{
|
||
|
InitPvar(pvarHit);
|
||
|
SetupChildren();
|
||
|
|
||
|
if (SendMessage(m_hwnd, WM_NCHITTEST, 0, MAKELONG(x, y)) == (m_fSysMenu ? HTSYSMENU : HTMENU))
|
||
|
{
|
||
|
pvarHit->vt = VT_I4;
|
||
|
pvarHit->lVal = 0;
|
||
|
|
||
|
if (m_cChildren)
|
||
|
{
|
||
|
if (m_fSysMenu)
|
||
|
pvarHit->lVal = 1;
|
||
|
else
|
||
|
{
|
||
|
POINT pt;
|
||
|
|
||
|
pt.x = x;
|
||
|
pt.y = y;
|
||
|
|
||
|
// MenuItemFromPoint conveniently returns -1 if we are not
|
||
|
// over any menu item, so that gets returned as 0 (CHILDID_SELF)
|
||
|
// while others get bumped by 1 to be 1..n. Cool!
|
||
|
pvarHit->lVal = MenuItemFromPoint(m_hwnd, m_hMenu, pt) + 1;
|
||
|
}
|
||
|
|
||
|
if (pvarHit->lVal)
|
||
|
{
|
||
|
IDispatch* pdispChild;
|
||
|
|
||
|
pdispChild = NULL;
|
||
|
get_accChild(*pvarHit, &pdispChild);
|
||
|
if (pdispChild)
|
||
|
{
|
||
|
pvarHit->vt = VT_DISPATCH;
|
||
|
pvarHit->pdispVal = pdispChild;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::accNavigate()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::accNavigate(long dwNavDir, VARIANT varStart, VARIANT* pvarEnd)
|
||
|
{
|
||
|
long lEnd = 0;
|
||
|
HMENU hSubMenu;
|
||
|
|
||
|
InitPvar(pvarEnd);
|
||
|
|
||
|
if (!ValidateChild(&varStart) ||
|
||
|
!ValidateNavDir(dwNavDir, varStart.lVal))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (dwNavDir == NAVDIR_FIRSTCHILD)
|
||
|
dwNavDir = NAVDIR_NEXT;
|
||
|
else if (dwNavDir == NAVDIR_LASTCHILD)
|
||
|
{
|
||
|
varStart.lVal = m_cChildren + 1;
|
||
|
dwNavDir = NAVDIR_PREVIOUS;
|
||
|
}
|
||
|
else if (varStart.lVal == CHILDID_SELF)
|
||
|
return(GetParentToNavigate((m_fSysMenu ? OBJID_SYSMENU : OBJID_MENU),
|
||
|
m_hwnd, OBJID_WINDOW, dwNavDir, pvarEnd));
|
||
|
|
||
|
// when we get to here, navdir was either firstchild
|
||
|
// or lastchild (now changed to either next or previous)
|
||
|
// OR
|
||
|
// we were starting from something other than the parent object
|
||
|
switch (dwNavDir)
|
||
|
{
|
||
|
case NAVDIR_RIGHT:
|
||
|
case NAVDIR_NEXT:
|
||
|
lEnd = varStart.lVal + 1;
|
||
|
if (lEnd > m_cChildren)
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_LEFT:
|
||
|
case NAVDIR_PREVIOUS:
|
||
|
lEnd = varStart.lVal - 1;
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_UP:
|
||
|
case NAVDIR_DOWN:
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (lEnd)
|
||
|
{
|
||
|
// we should give the child object back!!
|
||
|
#ifdef _DEBUG
|
||
|
if (!m_hMenu)
|
||
|
{
|
||
|
//DBPRINTF (TEXT("null hmenu at 2\r\n"));
|
||
|
Assert (m_hMenu);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
hSubMenu = GetSubMenu (m_hMenu,lEnd-1);
|
||
|
if (hSubMenu)
|
||
|
{
|
||
|
pvarEnd->vt=VT_DISPATCH;
|
||
|
return(CreateMenuItem((IAccessible*)this, m_hwnd, m_hMenu, hSubMenu,
|
||
|
lEnd, 0, FALSE, IID_IDispatch, (void**)&pvarEnd->pdispVal));
|
||
|
}
|
||
|
// just return VT_I4 if it does not have a submenu.
|
||
|
pvarEnd->vt = VT_I4;
|
||
|
pvarEnd->lVal = lEnd;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::accDoDefaultAction()
|
||
|
//
|
||
|
// Menu bars have no defaults. However, items do. Hierarchical items
|
||
|
// drop down/pop up their hierarchical. Non-hierarchical items execute
|
||
|
// their command. To Open something that is closed or to Execute a command,
|
||
|
// we use SendInput to send Alt+ShortcutKey. To Close something, we just
|
||
|
// send Alt.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::accDoDefaultAction(VARIANT varChild)
|
||
|
{
|
||
|
GUITHREADINFO gui;
|
||
|
TCHAR chHotKey;
|
||
|
HMENU hSubMenu;
|
||
|
int i,n;
|
||
|
int nTries;
|
||
|
#define MAX_TRIES 20
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
|
||
|
if( GetMDIButtonIndex( m_hMenu, varChild.lVal - 1 ) != 0 )
|
||
|
{
|
||
|
// Special case for MDI child buttons - they are actually implemented
|
||
|
// as menu items, but appear as buttons.
|
||
|
|
||
|
// Active them by posting a WM_COMMAND with the command id of the
|
||
|
// corresponding menu item.
|
||
|
//
|
||
|
// This WM_COMMAND gets picked up by the MDIClient window, and it then
|
||
|
// minimizes/restores/closes the currently active child. (If we did
|
||
|
// a WM_SYSCOMMAND instead of WM_COMMAND, the overall app window
|
||
|
// would minimize/restore/close instead!)
|
||
|
int id = GetMenuItemID( m_hMenu, varChild.lVal - 1 );
|
||
|
PostMessage(m_hwnd, WM_COMMAND, id, 0L);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Only if this window is active can we do the default.
|
||
|
//
|
||
|
if (!MyGetGUIThreadInfo(0, &gui))
|
||
|
return(E_FAIL);
|
||
|
|
||
|
if (m_hwnd != gui.hwndActive)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
// If disabled, fail
|
||
|
if (GetMenuState(m_hMenu, varChild.lVal-1, MF_BYPOSITION) & MFS_GRAYED)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
if (!m_hMenu)
|
||
|
{
|
||
|
//DBPRINTF (TEXT("null hmenu at 3\r\n"));
|
||
|
Assert (m_hMenu);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// First check if this item has a sub menu, and if it is open.
|
||
|
// If it has, and it is, then close it.
|
||
|
if (hSubMenu = GetSubMenu(m_hMenu, varChild.lVal-1))
|
||
|
{
|
||
|
if (GetSubMenuWindow(hSubMenu))
|
||
|
{
|
||
|
MyBlockInput (TRUE);
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0);
|
||
|
MyBlockInput (FALSE);
|
||
|
return (S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// when we get here, either it doesn't have a submenu and we need
|
||
|
// to execute, or the submenu is closed and we need to open it.
|
||
|
// Our actions are the same in either case. - send Alt+Letter if
|
||
|
// there is a letter, if not a letter....
|
||
|
|
||
|
// special case for system menus
|
||
|
if (m_fSysMenu)
|
||
|
{
|
||
|
LPARAM lParam;
|
||
|
|
||
|
if (GetWindowLong(m_hwnd, GWL_STYLE) & WS_CHILD)
|
||
|
lParam = MAKELONG('-', 0);
|
||
|
else
|
||
|
lParam = MAKELONG(' ', 0);
|
||
|
|
||
|
PostMessage(m_hwnd, WM_SYSCOMMAND, SC_KEYMENU, lParam);
|
||
|
return (S_OK);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get menu item string; get & character.
|
||
|
//
|
||
|
|
||
|
chHotKey = GetMenuItemHotkey( this, m_hwnd, m_hMenu, varChild.lVal, 0 );
|
||
|
|
||
|
if (chHotKey)
|
||
|
{
|
||
|
MyBlockInput (TRUE);
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0);
|
||
|
SendKey (KEYPRESS,VK_CHAR,0,chHotKey);
|
||
|
SendKey (KEYRELEASE,VK_CHAR,0,chHotKey);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0);
|
||
|
MyBlockInput (FALSE);
|
||
|
return (S_OK);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Bad Apps don't define hot keys. We can try to move the selection
|
||
|
// to that item and then hit enter. An easier way would be to just
|
||
|
// hit Alt+FirstLetter, but if there are more than 1 item with that
|
||
|
// letter, it will always do the first one. Not optimal, may lead to
|
||
|
// unexpected side-effects. Better to do nothing than to do that.
|
||
|
//
|
||
|
// We need to put ourselves in menu mode if we aren't already, then
|
||
|
// send right arrow keys to put us on the right one, then hit Enter.
|
||
|
// If we are already in menu mode, take us out of menu mode to close
|
||
|
// the heirarchy, then go back into menu mode and continue.
|
||
|
MyBlockInput (TRUE);
|
||
|
if (gui.flags & GUI_INMENUMODE)
|
||
|
{
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0);
|
||
|
}
|
||
|
|
||
|
// now go into menu mode and send right arrows until the one we
|
||
|
// want is highlighted.
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_MENU,0);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_MENU,0);
|
||
|
|
||
|
// calculate how many right arrows to hit:
|
||
|
n = varChild.lVal-1;
|
||
|
// if this menu is the menu of an MDI window and the window
|
||
|
// is maximized, then the thing now highlighted is the MDI
|
||
|
// Doc Sys menu, and we'll have to go 1 farther than we think.
|
||
|
// To see if this is the case, we'll check if the first item
|
||
|
// in the menu is something with a submenu and it is a bitmap menu.
|
||
|
if (GetSubMenu(m_hMenu,0) &&
|
||
|
(GetMenuState(m_hMenu, 0, MF_BYPOSITION) & MF_BITMAP))
|
||
|
n++;
|
||
|
|
||
|
for (i = 0; i < n;i++)
|
||
|
{
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_RIGHT,0);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_RIGHT,0);
|
||
|
}
|
||
|
MyBlockInput (FALSE);
|
||
|
|
||
|
// check if it is highlighted now. If so, hit enter to activate.
|
||
|
// try several times -
|
||
|
nTries = 0;
|
||
|
while ( ((GetMenuState(m_hMenu, varChild.lVal-1, MF_BYPOSITION) & MF_HILITE) == 0) &&
|
||
|
(nTries < MAX_TRIES))
|
||
|
{
|
||
|
Sleep(55);
|
||
|
nTries++;
|
||
|
}
|
||
|
|
||
|
if (GetMenuState(m_hMenu, varChild.lVal-1, MF_BYPOSITION) & MF_HILITE)
|
||
|
{
|
||
|
MyBlockInput (TRUE);
|
||
|
SendKey (KEYPRESS,VK_VIRTUAL,VK_RETURN,0);
|
||
|
SendKey (KEYRELEASE,VK_VIRTUAL,VK_RETURN,0);
|
||
|
MyBlockInput (FALSE);
|
||
|
return (S_OK);
|
||
|
}
|
||
|
else
|
||
|
return (E_FAIL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenu::Clone()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenu::Clone(IEnumVARIANT** ppenum)
|
||
|
{
|
||
|
return(CreateMenuBar(m_hwnd, m_fSysMenu, m_idChildCur, IID_IEnumVARIANT,
|
||
|
(void**)ppenum));
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CMenu::GetIdentityString (
|
||
|
DWORD dwIDChild,
|
||
|
BYTE ** ppIDString,
|
||
|
DWORD * pdwIDStringLen
|
||
|
)
|
||
|
{
|
||
|
*ppIDString = NULL;
|
||
|
*pdwIDStringLen = 0;
|
||
|
|
||
|
BYTE * pKeyData = (BYTE *) CoTaskMemAlloc( HMENUKEYSIZE );
|
||
|
if( ! pKeyData )
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
DWORD dwpid;
|
||
|
GetWindowThreadProcessId( m_hwnd, & dwpid );
|
||
|
MakeHmenuKey( pKeyData, dwpid, m_hMenu, dwIDChild );
|
||
|
|
||
|
*ppIDString = pKeyData;
|
||
|
*pdwIDStringLen = HMENUKEYSIZE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// MENU ITEMS
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateMenuItem()
|
||
|
//
|
||
|
// This creates a child object for a menu item that has a sub menu.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// paccMenu IN pointer to the parent's IAccessible
|
||
|
// hwnd IN the hwnd of the window that owns the parent menu
|
||
|
// hMenu IN the hmenu of the menu that owns this item.
|
||
|
// hSubMenu IN the hMenu of the submenu this menu item opens
|
||
|
// ItemID IN the menu item ID. Position (1..n).
|
||
|
// iCurChild IN ID of the current child in the enumeration
|
||
|
// fPopup IN is this menu item in a popup or on a menu bar?
|
||
|
// riid IN what interface are we asking for on this item?
|
||
|
// ppvItem OUT the pointer to the interface asked for.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateMenuItem(IAccessible* paccMenu, HWND hwnd, HMENU hMenu,
|
||
|
HMENU hSubMenu, long ItemID, long iCurChild, BOOL fPopup, REFIID riid,
|
||
|
void** ppvItem)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
CMenuItem* pmenuitem;
|
||
|
|
||
|
InitPv(ppvItem);
|
||
|
|
||
|
pmenuitem = new CMenuItem(paccMenu, hwnd, hMenu, hSubMenu, ItemID, iCurChild, fPopup);
|
||
|
if (! pmenuitem)
|
||
|
return(E_OUTOFMEMORY);
|
||
|
|
||
|
hr = pmenuitem->QueryInterface(riid, ppvItem);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
delete pmenuitem;
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::CMenuItem()
|
||
|
//
|
||
|
// We hang on to our parent object so we can forward methods up to him.
|
||
|
// Therefore we must bump up the ref count.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuItem::CMenuItem(IAccessible* paccParent, HWND hwnd, HMENU hMenu,
|
||
|
HMENU hSubMenu, long ItemID, long iCurChild, BOOL fPopup)
|
||
|
: CAccessible( CLASS_MenuItemObject )
|
||
|
{
|
||
|
m_hwnd = hwnd;
|
||
|
m_hMenu = hMenu;
|
||
|
m_hSubMenu = hSubMenu;
|
||
|
m_ItemID = ItemID;
|
||
|
m_idChildCur = iCurChild;
|
||
|
m_fInAPopup = fPopup;
|
||
|
|
||
|
m_paccParent = paccParent;
|
||
|
paccParent->AddRef();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::~CMenuItem()
|
||
|
//
|
||
|
// We hung on to our parent, so we must release it on destruction.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuItem::~CMenuItem()
|
||
|
{
|
||
|
m_paccParent->Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// SetupChildren()
|
||
|
//
|
||
|
// CMenuItems have 1 child. That one child is either a CMenuPopupFrame or
|
||
|
// a CMenuPopup (depending if the menu is visible).
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
void CMenuItem::SetupChildren(void)
|
||
|
{
|
||
|
m_cChildren = 1;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accParent()
|
||
|
//
|
||
|
// Pass it on back to the parent.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accParent(IDispatch** ppdispParent)
|
||
|
{
|
||
|
InitPv(ppdispParent);
|
||
|
|
||
|
return(m_paccParent->QueryInterface(IID_IDispatch, (void**)ppdispParent));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accChild()
|
||
|
//
|
||
|
// The menu item's child is either a CMenuPopupFrame (if the popup window
|
||
|
// is visible and belongs to this CMenuItem) or a CMenuPopup. This allows
|
||
|
// someone to enumerate the commands whether or not the popup is visible.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accChild(VARIANT varChild, IDispatch** ppdispChild)
|
||
|
{
|
||
|
HWND hwndSubMenu;
|
||
|
HRESULT hr;
|
||
|
|
||
|
InitPv(ppdispChild);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal != CHILDID_SELF)
|
||
|
{
|
||
|
// In order to create the accessible object representing the child,
|
||
|
// we have to find the popup menu window.
|
||
|
// Once we have found it, we check if it is visible. If so, then
|
||
|
// our child is a CMenuPopupFrame, which we will create by
|
||
|
// calling CreateMenuPopupWindow.
|
||
|
// If the popup window is not visible, or if it does not belong
|
||
|
// to this CMenuItem, then our child is a CMenuPopup, which we
|
||
|
// will create by calling CreateMenuPopup.
|
||
|
//
|
||
|
hwndSubMenu = GetSubMenuWindow (m_hSubMenu);
|
||
|
if (hwndSubMenu)
|
||
|
return (CreateMenuPopupWindow (hwndSubMenu,0,IID_IDispatch, (void**)ppdispChild));
|
||
|
else
|
||
|
{
|
||
|
// this is where we create 'invisible' popups so apps can
|
||
|
// walk down and see all of the commands (most, at least).
|
||
|
// Since it is invisible, we have to tell it more about who
|
||
|
// it's parent is.
|
||
|
hr = CreateMenuPopupClient (NULL,0,IID_IDispatch,(void**)ppdispChild);
|
||
|
if (SUCCEEDED (hr))
|
||
|
((CMenuPopup*)*ppdispChild)->SetParentInfo((IAccessible*)this,
|
||
|
m_hSubMenu,varChild.lVal);
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accName()
|
||
|
//
|
||
|
// The name for the child (CMenuPopup or CMenuPopupFrame) is the same as
|
||
|
// the name of the Parent/Self, so whether we are asked for id=self or
|
||
|
// id = child (1), we return the same thing.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accName(VARIANT varChild, BSTR* pszName)
|
||
|
{
|
||
|
InitPv(pszName);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
return GetMenuItemName( this, m_hwnd, m_hMenu, m_ItemID, pszName );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accRole()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accRole(VARIANT varChild, VARIANT* pvarRole)
|
||
|
{
|
||
|
MENUITEMINFO mi;
|
||
|
|
||
|
InitPvar(pvarRole);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
pvarRole->vt = VT_I4;
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
mi.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mi.fMask = MIIM_TYPE | MIIM_SUBMENU;
|
||
|
mi.cch = 0;
|
||
|
mi.dwTypeData = 0;
|
||
|
|
||
|
GetMenuItemInfo(m_hMenu, m_ItemID-1, TRUE, &mi);
|
||
|
if (mi.fType & MFT_SEPARATOR)
|
||
|
pvarRole->lVal = ROLE_SYSTEM_SEPARATOR;
|
||
|
else
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUITEM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUPOPUP;
|
||
|
}
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accState()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accState(VARIANT varChild, VARIANT* pvarState)
|
||
|
{
|
||
|
HWND hwndSubMenu;
|
||
|
|
||
|
InitPvar(pvarState);
|
||
|
|
||
|
if (!ValidateChild (&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
// We do this because sometimes we'll be asked for our own info,
|
||
|
// and the caller will just call us item 0 (CHILDID_SELF) and we
|
||
|
// have to make sure when we call our parent to tell her who
|
||
|
// we are (m_ItemID).
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
varChild.lVal = m_ItemID;
|
||
|
return(m_paccParent->get_accState(varChild, pvarState));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If the popup (our only child) is not showing or it belongs to
|
||
|
// another menu item, set the state to invisible.
|
||
|
// If it is showing and belongs to us, set the state to normal.
|
||
|
|
||
|
// This starts by assuming that it is invisible, and clearing the
|
||
|
// state if we find a visible menu that belongs to us.
|
||
|
pvarState->vt = VT_I4;
|
||
|
pvarState->lVal = 0 | STATE_SYSTEM_INVISIBLE;
|
||
|
|
||
|
hwndSubMenu = GetSubMenuWindow (m_hSubMenu);
|
||
|
if (hwndSubMenu)
|
||
|
pvarState->lVal = 0;
|
||
|
}
|
||
|
return (S_OK);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accKeyboardShortcut()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszShortcut)
|
||
|
{
|
||
|
InitPv(pszShortcut);
|
||
|
|
||
|
if (! ValidateChild(&varChild))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
// Get menu item shortcut - use "Alt+" format for menu bars...
|
||
|
BOOL fIsMenuBar = m_hwnd && ::GetMenu( m_hwnd ) == m_hMenu;
|
||
|
|
||
|
return GetMenuItemShortcut( this, m_hwnd, m_hMenu, m_ItemID, fIsMenuBar, pszShortcut );
|
||
|
}
|
||
|
|
||
|
return S_FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accFocus()
|
||
|
//
|
||
|
// If focus is us or our popup, great.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accFocus(VARIANT* pvarFocus)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
HWND hwndSubMenu;
|
||
|
IDispatch* pdispChild;
|
||
|
|
||
|
// Ask our parent who has the focus. Is it us?
|
||
|
hr = m_paccParent->get_accFocus(pvarFocus);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
return(hr);
|
||
|
|
||
|
// No, so return nothing.
|
||
|
if ((pvarFocus->vt != VT_I4) || (pvarFocus->lVal != m_ItemID))
|
||
|
{
|
||
|
VariantClear(pvarFocus);
|
||
|
pvarFocus->vt = VT_EMPTY;
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
// Is the currently active popup our child?
|
||
|
// If so, then we should return an IDispatch to
|
||
|
// the window frame object.
|
||
|
hwndSubMenu = GetSubMenuWindow (m_hSubMenu);
|
||
|
if (hwndSubMenu)
|
||
|
{
|
||
|
hr = CreateMenuPopupWindow (hwndSubMenu,0,IID_IDispatch,(void**)&pdispChild);
|
||
|
|
||
|
if (!SUCCEEDED(hr))
|
||
|
return (hr);
|
||
|
pvarFocus->vt = VT_DISPATCH;
|
||
|
pvarFocus->pdispVal = pdispChild;
|
||
|
return (S_OK);
|
||
|
}
|
||
|
|
||
|
pvarFocus->lVal = 0;
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::get_accDefaultAction()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::get_accDefaultAction(VARIANT varChild, BSTR* pszDefA)
|
||
|
{
|
||
|
InitPv(pszDefA);
|
||
|
|
||
|
if (!ValidateChild (&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
// We do this because sometimes we'll be asked for our own info,
|
||
|
// and the caller will just call us item 0 (CHILDID_SELF) and we
|
||
|
// have to make sure when we call our parent to tell her who
|
||
|
// we are (m_ItemID).
|
||
|
// But sometimes, we will be asked for info about our child - There
|
||
|
// is no default action for our child.
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
varChild.lVal = m_ItemID;
|
||
|
return(m_paccParent->get_accDefaultAction(varChild, pszDefA));
|
||
|
}
|
||
|
return (E_NOT_APPLICABLE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::accSelect()
|
||
|
//
|
||
|
// We just let our parent take care of this for us. Tell her who we are by
|
||
|
// setting varChild.lVal to our ItemID.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::accSelect(long flagsSel, VARIANT varChild)
|
||
|
{
|
||
|
if (!ValidateChild (&varChild) ||
|
||
|
!ValidateSelFlags(flagsSel))
|
||
|
return (E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
varChild.lVal = m_ItemID;
|
||
|
return(m_paccParent->accSelect(flagsSel, varChild));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::accLocation()
|
||
|
//
|
||
|
// Sometimes we are asked for the location of a peer object. This is
|
||
|
// kinda screwy. This happens when we are asked to navigate next or prev,
|
||
|
// and then let our parent navigate for us. The caller then starts thinking
|
||
|
// we know about our peers.
|
||
|
// Since this is the only case where something like this happens, we'll
|
||
|
// have to do some sort of hack.
|
||
|
// Problem is, when they ask for a child 0 (self) we are OK.
|
||
|
// But when we are asked for child 1, is it the popup or peer 1?
|
||
|
// I am going to assume that it is always the peer.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::accLocation(long* pxLeft, long* pyTop,
|
||
|
long* pcxWidth, long* pcyHeight, VARIANT varChild)
|
||
|
{
|
||
|
// we just call this to translate empty values - not going to
|
||
|
// check the return value.
|
||
|
ValidateChild (&varChild);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
varChild.lVal = m_ItemID;
|
||
|
|
||
|
return(m_paccParent->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::accNavigate()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::accNavigate(long dwNavDir, VARIANT varStart,
|
||
|
VARIANT* pvarEnd)
|
||
|
{
|
||
|
HWND hwndSubMenu;
|
||
|
|
||
|
InitPvar(pvarEnd);
|
||
|
|
||
|
if (!ValidateChild(&varStart))
|
||
|
return (E_INVALIDARG);
|
||
|
|
||
|
if (!ValidateNavDir(dwNavDir, varStart.lVal))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (dwNavDir >= NAVDIR_FIRSTCHILD) // this means firstchild or lastchild
|
||
|
{
|
||
|
hwndSubMenu = GetSubMenuWindow (m_hSubMenu);
|
||
|
if (hwndSubMenu)
|
||
|
{
|
||
|
pvarEnd->vt = VT_DISPATCH;
|
||
|
return (CreateMenuPopupWindow (hwndSubMenu,0,IID_IDispatch, (void**)&(pvarEnd->pdispVal)));
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (varStart.lVal == CHILDID_SELF)
|
||
|
varStart.lVal = m_ItemID;
|
||
|
return(m_paccParent->accNavigate(dwNavDir, varStart, pvarEnd));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::accHitTest()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::accHitTest(long x, long y, VARIANT* pvarHit)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
HWND hwndSubMenu;
|
||
|
RECT rc;
|
||
|
POINT pt;
|
||
|
|
||
|
InitPvar(pvarHit);
|
||
|
|
||
|
hwndSubMenu = GetSubMenuWindow (m_hSubMenu);
|
||
|
if (hwndSubMenu)
|
||
|
{
|
||
|
// Is point in our popup menu window child?
|
||
|
MyGetRect(hwndSubMenu, &rc, TRUE);
|
||
|
|
||
|
pt.x = x;
|
||
|
pt.y = y;
|
||
|
|
||
|
if (PtInRect(&rc, pt))
|
||
|
{
|
||
|
// need to set the parent
|
||
|
pvarHit->vt = VT_DISPATCH;
|
||
|
return (CreateMenuPopupWindow (hwndSubMenu,0,IID_IDispatch, (void**)pvarHit->pdispVal));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Is point in us?
|
||
|
hr = m_paccParent->accHitTest(x, y, pvarHit);
|
||
|
// #11150, CWO, 1/24/97, changed from !SUCCEEDED to !S_OK
|
||
|
if ((hr != S_OK) || (pvarHit->vt == VT_EMPTY))
|
||
|
return(hr);
|
||
|
|
||
|
pvarHit->vt = VT_I4;
|
||
|
pvarHit->lVal = CHILDID_SELF;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::accDoDefaultAction()
|
||
|
//
|
||
|
// We just let our parent take care of this for us. Tell her who we are by
|
||
|
// setting varChild.lVal to our ItemID.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::accDoDefaultAction(VARIANT varChild)
|
||
|
{
|
||
|
if (! ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
Assert(varChild.vt == VT_I4);
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
varChild.lVal = m_ItemID;
|
||
|
return(m_paccParent->accDoDefaultAction(varChild));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuItem::Clone()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuItem::Clone(IEnumVARIANT** ppenum)
|
||
|
{
|
||
|
return(CreateMenuItem(m_paccParent, m_hwnd, m_hMenu, m_hSubMenu,m_ItemID,
|
||
|
m_idChildCur, FALSE, IID_IEnumVARIANT, (void**)ppenum));
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CMenuItem::GetIdentityString (
|
||
|
DWORD dwIDChild,
|
||
|
BYTE ** ppIDString,
|
||
|
DWORD * pdwIDStringLen
|
||
|
)
|
||
|
{
|
||
|
*ppIDString = NULL;
|
||
|
*pdwIDStringLen = 0;
|
||
|
|
||
|
if( dwIDChild != CHILDID_SELF )
|
||
|
{
|
||
|
// CMenuItems have 1 child - that one child is either a CMenuPopupFrame or
|
||
|
// a CMenuPopup (depending if the menu is visible).
|
||
|
// We're not going to support geting the IDs of these thourhg the parent,
|
||
|
// a client should get the interface pointers to those objects themselves,
|
||
|
// and ask them for their string.
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Weird stuff alert:
|
||
|
//
|
||
|
// CMenuItem repesents a menu item that has an associated popup. (ie. it is
|
||
|
// not a command leaf node) There are two options for representing this item:
|
||
|
//
|
||
|
// as a child of its parent menu,
|
||
|
// or
|
||
|
// as the parent (CHILDID_SELF) of its own submenu
|
||
|
//
|
||
|
// While the HWND-based controls use the latter option, here we are going for
|
||
|
// the former - this keeps the menu item with its siblings, regardless of
|
||
|
// whether they are menu items with popups, or leaf-node commands.
|
||
|
// We can do this here since we do know what our parent HMENU is and what our
|
||
|
// child id is in that parent menu.
|
||
|
// HWND-based proxies as generally don't have this information available to them,
|
||
|
// so the former option is not really an option for them.
|
||
|
|
||
|
// This request is for the item itself - represent it as a child of
|
||
|
// our parent menu
|
||
|
|
||
|
BYTE * pKeyData = (BYTE *) CoTaskMemAlloc( HMENUKEYSIZE );
|
||
|
if( ! pKeyData )
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// Need to find pid of process that the menu belongs to. Can't use the
|
||
|
// pid of the popup menu, since that's a shared/reused system window.
|
||
|
// Instead, we assume that since the menu is present, it belongs to the
|
||
|
// current foreground thread, which is what GetGUIThreadInfo(NULL) gets us.
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
if( ! MyGetGUIThreadInfo( NULL, & GuiThreadInfo ) )
|
||
|
return E_FAIL;
|
||
|
DWORD dwPid = 0;
|
||
|
GetWindowThreadProcessId( GuiThreadInfo.hwndActive, & dwPid );
|
||
|
if( dwPid == 0 )
|
||
|
return E_FAIL;
|
||
|
|
||
|
MakeHmenuKey( pKeyData, dwPid, m_hMenu, m_ItemID );
|
||
|
|
||
|
*ppIDString = pKeyData;
|
||
|
*pdwIDStringLen = HMENUKEYSIZE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// MENU POPUPS
|
||
|
//
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateMenuPopupClient()
|
||
|
//
|
||
|
// EXTERNAL for CreateClientObject...
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateMenuPopupClient(HWND hwnd, long idChildCur,
|
||
|
REFIID riid, void** ppvPopup)
|
||
|
{
|
||
|
CMenuPopup* ppopup;
|
||
|
HRESULT hr;
|
||
|
|
||
|
ppopup = new CMenuPopup(hwnd, idChildCur);
|
||
|
if (!ppopup)
|
||
|
return(E_OUTOFMEMORY);
|
||
|
|
||
|
hr = ppopup->QueryInterface(riid, ppvPopup);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
delete ppopup;
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::CMenuPopup()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuPopup::CMenuPopup(HWND hwnd, long idChildCur)
|
||
|
: CClient( CLASS_MenuPopupClient )
|
||
|
{
|
||
|
Initialize(hwnd, idChildCur);
|
||
|
m_hMenu = NULL;
|
||
|
m_ItemID = 0;
|
||
|
m_hwndParent = NULL;
|
||
|
m_fSonOfPopup = 0;
|
||
|
m_fSysMenu = 0;
|
||
|
|
||
|
// this only works if there is a window handle.
|
||
|
if (hwnd)
|
||
|
{
|
||
|
m_hMenu = (HMENU)SendMessage (m_hwnd,MN_GETHMENU,0,0);
|
||
|
// if we didn't get back an HMENU, that means that the window
|
||
|
// is probably invisible. Don't try to set other values.
|
||
|
// SetupChildren will see this and set m_cChildren to 0.
|
||
|
if (m_hMenu)
|
||
|
{
|
||
|
m_ItemID = FindItemIDThatOwnsThisMenu (m_hMenu,&m_hwndParent,
|
||
|
&m_fSonOfPopup,&m_fSysMenu);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// The CMenuPopup objects need to know their parent when they are invisible,
|
||
|
// so after one is created, the creator should call SetParentInfo.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
void CMenuPopup::SetParentInfo(IAccessible* paccParent,HMENU hMenu,long ItemID)
|
||
|
{
|
||
|
m_paccParent = paccParent;
|
||
|
m_hMenu = hMenu;
|
||
|
m_ItemID= ItemID;
|
||
|
if (paccParent)
|
||
|
paccParent->AddRef();
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::~CMenuPopup()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuPopup::~CMenuPopup(void)
|
||
|
{
|
||
|
if (m_paccParent)
|
||
|
m_paccParent->Release();
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::SetupChildren()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
void CMenuPopup::SetupChildren(void)
|
||
|
{
|
||
|
// we need to be able to set up our children whether the popup is
|
||
|
// displayed or not. So we have a m_hMenu variable, it just needs
|
||
|
// to be set when the thing is made - It is either set by the
|
||
|
// constructor (if we are visible) or by the dude that called the create
|
||
|
// function if we are invisible.
|
||
|
// PROBLEM - sometimes CMenuPopups are created by a call to
|
||
|
// AccessibleObjectFromEvent, and the hwnd isn't always able to
|
||
|
// give us back a good m_hMenu. So we will just set m_cChildren to 0.
|
||
|
if (m_hMenu)
|
||
|
{
|
||
|
m_cChildren = GetMenuItemCount(m_hMenu);
|
||
|
|
||
|
if( m_cChildren == -1 )
|
||
|
{
|
||
|
// Paranoia in case we get an invalid HMENU
|
||
|
m_cChildren = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
m_cChildren = 0;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accParent()
|
||
|
//
|
||
|
// The parent of a CMenuPopup is either a CMenuPopupFrame or a CMenuItem.
|
||
|
// If the popup is visible, it will have an hwnd, and lots of other stuff
|
||
|
// will also be set. If it is not visible, it will not have an hwnd.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accParent(IDispatch** ppdispParent)
|
||
|
{
|
||
|
if (m_paccParent)
|
||
|
{
|
||
|
return (m_paccParent->QueryInterface(IID_IDispatch,(void**)ppdispParent));
|
||
|
}
|
||
|
else if (m_hwnd)
|
||
|
{
|
||
|
// try to create a parent for us...
|
||
|
return (CreateMenuPopupWindow (m_hwnd,0,IID_IDispatch,(void**)ppdispParent));
|
||
|
}
|
||
|
else
|
||
|
return (E_FAIL);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accChild()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accChild(VARIANT varChild, IDispatch** ppdispChild)
|
||
|
{
|
||
|
HMENU hSubMenu;
|
||
|
|
||
|
InitPv(ppdispChild);
|
||
|
|
||
|
if (!ValidateChild(&varChild) || varChild.lVal == CHILDID_SELF)
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
//
|
||
|
// Is this item a hierarchical?
|
||
|
//
|
||
|
Assert (m_hMenu);
|
||
|
hSubMenu = GetSubMenu(m_hMenu, varChild.lVal-1);
|
||
|
if (!hSubMenu)
|
||
|
return(S_FALSE);
|
||
|
|
||
|
//
|
||
|
// Yes.
|
||
|
//
|
||
|
return(CreateMenuItem((IAccessible*)this, m_hwnd, m_hMenu, hSubMenu,
|
||
|
varChild.lVal, 0, FALSE, IID_IDispatch, (void**)ppdispChild));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accName()
|
||
|
//
|
||
|
// The name of the popup is the name of the item it hangs off of.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accName(VARIANT varChild, BSTR* pszName)
|
||
|
{
|
||
|
HWND hwndOwner;
|
||
|
TCHAR szClassName[50];
|
||
|
HRESULT hr;
|
||
|
IAccessible* paccParent;
|
||
|
|
||
|
InitPv(pszName);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
// If we popped up from a menu bar or as another popup,
|
||
|
// then our name is the name of the thing that popped us
|
||
|
// up. If we are a floating popup, then our name is...?
|
||
|
//
|
||
|
// We implement this by either:
|
||
|
// 1. calling our parent object, OR
|
||
|
// 2. creating a parent object on the fly that we can
|
||
|
// ask the name of, OR
|
||
|
// 3. Looking for the name of the owner window, OR
|
||
|
// 4. Checking if we are the child of the start button.
|
||
|
// If all else fails, we'll just call ourselves "context menu".
|
||
|
if (m_paccParent && m_ItemID)
|
||
|
{
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = m_ItemID;
|
||
|
return (m_paccParent->get_accName (varChild,pszName));
|
||
|
}
|
||
|
if (m_hwndParent && m_ItemID)
|
||
|
{
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = m_ItemID;
|
||
|
|
||
|
if (m_fSonOfPopup)
|
||
|
hr = CreateMenuPopupClient(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
else if (m_fSysMenu)
|
||
|
hr = CreateSysMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
else
|
||
|
hr = CreateMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = paccParent->get_accName (varChild,pszName);
|
||
|
paccParent->Release();
|
||
|
}
|
||
|
return (hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Try to get the owner window and use that for a name
|
||
|
// This doesn't seem to work on anything I have ever found,
|
||
|
// but it should work if anything has an owner, so i'll
|
||
|
//leav it in. If it starts breaking, just rip it out.
|
||
|
if (m_hwnd)
|
||
|
{
|
||
|
IAccessible* pacc;
|
||
|
HRESULT hr;
|
||
|
|
||
|
hwndOwner = ::GetWindow (m_hwnd,GW_OWNER);
|
||
|
hr = AccessibleObjectFromWindow (hwndOwner, OBJID_WINDOW, IID_IAccessible, (void**)&pacc);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pacc->get_accName(varChild,pszName);
|
||
|
pacc->Release();
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check if the start button has focus
|
||
|
hwndOwner = MyGetFocus();
|
||
|
if (InTheShell(hwndOwner, SHELL_TRAY))
|
||
|
{
|
||
|
GetClassName(hwndOwner,szClassName,ARRAYSIZE(szClassName));
|
||
|
if (lstrcmp(szClassName,TEXT("Button")) == 0)
|
||
|
{
|
||
|
return (HrCreateString(STR_STARTBUTTON,pszName));
|
||
|
}
|
||
|
}
|
||
|
// at least return this for a name
|
||
|
return (HrCreateString (STR_CONTEXT_MENU,pszName));
|
||
|
} // end else we don't have m_paccparent && m_itemid
|
||
|
} // end if childid_self
|
||
|
else // not childid self, childid > 0
|
||
|
{
|
||
|
return GetMenuItemName( this, m_hwnd, m_hMenu, varChild.lVal, pszName );
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accDescription()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accDescription(VARIANT varChild, BSTR* pszDesc)
|
||
|
{
|
||
|
InitPv(pszDesc);
|
||
|
|
||
|
if (! ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(CClient::get_accDescription(varChild, pszDesc));
|
||
|
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accRole()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accRole(VARIANT varChild, VARIANT* pvarRole)
|
||
|
{
|
||
|
InitPvar(pvarRole);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
pvarRole->vt = VT_I4;
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUPOPUP;
|
||
|
else
|
||
|
{
|
||
|
MENUITEMINFO mi;
|
||
|
|
||
|
mi.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mi.fMask = MIIM_TYPE;
|
||
|
mi.cch = 0;
|
||
|
mi.dwTypeData = 0;
|
||
|
|
||
|
if (GetMenuItemInfo(m_hMenu, varChild.lVal-1, TRUE, &mi) &&
|
||
|
(mi.fType & MFT_SEPARATOR))
|
||
|
pvarRole->lVal = ROLE_SYSTEM_SEPARATOR;
|
||
|
else
|
||
|
pvarRole->lVal = ROLE_SYSTEM_MENUITEM;
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accState()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accState(VARIANT varChild, VARIANT* pvarState)
|
||
|
{
|
||
|
InitPvar(pvarState);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
pvarState->vt = VT_I4;
|
||
|
pvarState->lVal = 0;
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(CClient::get_accState(varChild, pvarState));
|
||
|
else
|
||
|
{
|
||
|
// GetMenuBarInfo fails if the menu isn't currently present.
|
||
|
// We don't fail outright when this happens, since we still want
|
||
|
// to collect other info using GetMenuItemInfo below.
|
||
|
MENUBARINFO mbi;
|
||
|
if( MyGetMenuBarInfo(m_hwnd, OBJID_CLIENT, varChild.lVal, &mbi) )
|
||
|
{
|
||
|
if (mbi.fFocused)
|
||
|
{
|
||
|
pvarState->lVal |= STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get menu item flags. NOTE: Can't use GetMenuState(). It whacks
|
||
|
// random stuff in for hierarchicals.
|
||
|
//
|
||
|
MENUITEMINFO mi;
|
||
|
mi.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mi.fMask = MIIM_STATE | MIIM_SUBMENU;
|
||
|
|
||
|
if (!GetMenuItemInfo(m_hMenu, varChild.lVal-1, TRUE, &mi))
|
||
|
{
|
||
|
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
if (mi.fState & MFS_GRAYED)
|
||
|
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
|
||
|
|
||
|
if (mi.fState & MFS_CHECKED)
|
||
|
pvarState->lVal |= STATE_SYSTEM_CHECKED;
|
||
|
|
||
|
if (mi.fState & MFS_DEFAULT)
|
||
|
pvarState->lVal |= STATE_SYSTEM_DEFAULT;
|
||
|
|
||
|
if (mi.hSubMenu)
|
||
|
pvarState->lVal |= STATE_SYSTEM_HASPOPUP;
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accKeyboardShortcut()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszShortcut)
|
||
|
{
|
||
|
InitPv(pszShortcut);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return CClient::get_accKeyboardShortcut(varChild, pszShortcut);
|
||
|
|
||
|
// Get menu item shortcut. FALSE means don't use ALT+ form.
|
||
|
return GetMenuItemShortcut( this, m_hwnd, m_hMenu, varChild.lVal, FALSE, pszShortcut );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accFocus()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accFocus(VARIANT* pvarFocus)
|
||
|
{
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
MENUITEMINFO mii;
|
||
|
int i;
|
||
|
|
||
|
// set it to empty
|
||
|
if (IsBadWritePtr(pvarFocus,sizeof(VARIANT*)))
|
||
|
return (E_INVALIDARG);
|
||
|
|
||
|
InitPvar(pvarFocus);
|
||
|
|
||
|
//
|
||
|
// Are we in menu mode? If not, nothing.
|
||
|
//
|
||
|
if (!MyGetGUIThreadInfo (NULL,&GuiThreadInfo))
|
||
|
return(S_FALSE);
|
||
|
|
||
|
if (GuiThreadInfo.flags & GUI_INMENUMODE)
|
||
|
{
|
||
|
// do I have to loop through all of them to see which
|
||
|
// one is hilited?? Looks like it...
|
||
|
mii.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mii.fMask = MIIM_STATE;
|
||
|
|
||
|
SetupChildren();
|
||
|
for (i=0;i < m_cChildren;i++)
|
||
|
{
|
||
|
GetMenuItemInfo (m_hMenu,i,TRUE,&mii);
|
||
|
if (mii.fState & MFS_HILITE)
|
||
|
{
|
||
|
pvarFocus->vt = VT_I4;
|
||
|
pvarFocus->lVal = i+1;
|
||
|
return (S_OK);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// I don't think this should happen
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::get_accDefaultAction()
|
||
|
//
|
||
|
// Popups have no defaults. However, items do. Hierarchical items
|
||
|
// drop down/pop up their hierarchical. Non-hierarchical items execute
|
||
|
// their command.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::get_accDefaultAction(VARIANT varChild, BSTR* pszDefA)
|
||
|
{
|
||
|
HMENU hSubMenu;
|
||
|
|
||
|
InitPv(pszDefA);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
varChild.lVal--;
|
||
|
|
||
|
// Is this item enabled?
|
||
|
if (GetMenuState(m_hMenu, varChild.lVal, MF_BYPOSITION) & MFS_GRAYED)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
|
||
|
// Now check if this item has a submenu that is displayed.
|
||
|
// If there is, the action is hide, if not, the action is show.
|
||
|
// If it doesn't have a submenu, the action is execute.
|
||
|
#ifdef _DEBUG
|
||
|
if (!m_hMenu)
|
||
|
{
|
||
|
//DBPRINTF ("null hmenu at 4\r\n");
|
||
|
Assert (m_hMenu);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (hSubMenu = GetSubMenu(m_hMenu, varChild.lVal))
|
||
|
{
|
||
|
if (GetSubMenuWindow(hSubMenu))
|
||
|
return(HrCreateString(STR_DROPDOWN_HIDE, pszDefA));
|
||
|
else
|
||
|
return(HrCreateString(STR_DROPDOWN_SHOW, pszDefA));
|
||
|
}
|
||
|
else
|
||
|
return(HrCreateString(STR_EXECUTE, pszDefA));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::accSelect()
|
||
|
//
|
||
|
// We only accept TAKEFOCUS.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::accSelect(long flagsSel, VARIANT varChild)
|
||
|
{
|
||
|
if (!ValidateChild(&varChild) || !ValidateSelFlags(flagsSel))
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
if (flagsSel != SELFLAG_TAKEFOCUS)
|
||
|
return E_NOT_APPLICABLE;
|
||
|
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::accLocation()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
|
||
|
long* pcyHeight, VARIANT varChild)
|
||
|
{
|
||
|
MENUBARINFO mbi;
|
||
|
|
||
|
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(CClient::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
|
||
|
|
||
|
if (!MyGetMenuBarInfo(m_hwnd, OBJID_CLIENT, varChild.lVal, &mbi))
|
||
|
return(S_FALSE);
|
||
|
|
||
|
*pcyHeight = mbi.rcBar.bottom - mbi.rcBar.top;
|
||
|
*pcxWidth = mbi.rcBar.right - mbi.rcBar.left;
|
||
|
|
||
|
*pyTop = mbi.rcBar.top;
|
||
|
*pxLeft = mbi.rcBar.left;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::accHitTest()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::accHitTest(long x, long y, VARIANT* pvarHit)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
// first make sure we are pointing to our own client area
|
||
|
hr = CClient::accHitTest(x, y, pvarHit);
|
||
|
// #11150, CWO, 1/27/97, Replaced !SUCCEEDED with !S_OK
|
||
|
if ((hr != S_OK) || (pvarHit->vt != VT_I4) || (pvarHit->lVal != 0))
|
||
|
return(hr);
|
||
|
|
||
|
// now we can see which child is at this point.
|
||
|
SetupChildren();
|
||
|
|
||
|
if (m_cChildren)
|
||
|
{
|
||
|
POINT pt;
|
||
|
|
||
|
pt.x = x;
|
||
|
pt.y = y;
|
||
|
|
||
|
pvarHit->lVal = MenuItemFromPoint(m_hwnd, m_hMenu, pt) + 1;
|
||
|
|
||
|
if (pvarHit->lVal)
|
||
|
{
|
||
|
IDispatch* pdispChild;
|
||
|
|
||
|
pdispChild = NULL;
|
||
|
get_accChild(*pvarHit, &pdispChild);
|
||
|
if (pdispChild)
|
||
|
{
|
||
|
pvarHit->vt = VT_DISPATCH;
|
||
|
pvarHit->pdispVal = pdispChild;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::accNavigate()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::accNavigate(long dwNavDir, VARIANT varStart, VARIANT* pvarEnd)
|
||
|
{
|
||
|
long lEnd = 0;
|
||
|
MENUITEMINFO mi;
|
||
|
|
||
|
InitPvar(pvarEnd);
|
||
|
|
||
|
if (!ValidateChild(&varStart) ||
|
||
|
!ValidateNavDir(dwNavDir, varStart.lVal))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (dwNavDir == NAVDIR_FIRSTCHILD)
|
||
|
dwNavDir = NAVDIR_NEXT;
|
||
|
else if (dwNavDir == NAVDIR_LASTCHILD)
|
||
|
{
|
||
|
varStart.lVal = m_cChildren + 1;
|
||
|
dwNavDir = NAVDIR_PREVIOUS;
|
||
|
}
|
||
|
else if (!varStart.lVal)
|
||
|
{
|
||
|
return(CClient::accNavigate(dwNavDir, varStart, pvarEnd));
|
||
|
}
|
||
|
|
||
|
switch (dwNavDir)
|
||
|
{
|
||
|
case NAVDIR_NEXT:
|
||
|
case NAVDIR_DOWN:
|
||
|
lEnd = varStart.lVal + 1;
|
||
|
if (lEnd > m_cChildren)
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_PREVIOUS:
|
||
|
case NAVDIR_UP:
|
||
|
lEnd = varStart.lVal - 1;
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_LEFT:
|
||
|
case NAVDIR_RIGHT:
|
||
|
lEnd = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (lEnd)
|
||
|
{
|
||
|
// we should give the child object back!!
|
||
|
// can't use getSubMenu here because it seems to ignore
|
||
|
// separators??
|
||
|
//hSubMenu = GetSubMenu (m_hMenu,lEnd-1);
|
||
|
|
||
|
mi.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mi.fMask = MIIM_SUBMENU;
|
||
|
mi.cch = 0;
|
||
|
mi.dwTypeData = 0;
|
||
|
GetMenuItemInfo (m_hMenu,lEnd-1,TRUE,&mi);
|
||
|
if (mi.hSubMenu)
|
||
|
{
|
||
|
pvarEnd->vt=VT_DISPATCH;
|
||
|
return(CreateMenuItem((IAccessible*)this, m_hwnd, m_hMenu, mi.hSubMenu,
|
||
|
lEnd, 0, FALSE, IID_IDispatch, (void**)&pvarEnd->pdispVal));
|
||
|
}
|
||
|
// just return VT_I4 if it does not have a submenu.
|
||
|
pvarEnd->vt = VT_I4;
|
||
|
pvarEnd->lVal = lEnd;
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
return(S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::accDoDefaultAction()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::accDoDefaultAction(VARIANT varChild)
|
||
|
{
|
||
|
RECT rcLoc;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
return(CClient::accDoDefaultAction(varChild));
|
||
|
|
||
|
// If disabled, fail
|
||
|
if (GetMenuState(m_hMenu, varChild.lVal-1, MF_BYPOSITION) & MFS_GRAYED)
|
||
|
return(E_NOT_APPLICABLE);
|
||
|
|
||
|
hr = accLocation(&rcLoc.left,&rcLoc.top,&rcLoc.right,&rcLoc.bottom,varChild);
|
||
|
if (!SUCCEEDED (hr))
|
||
|
return (hr);
|
||
|
|
||
|
// this will check if WindowFromPoint at the click point is the same
|
||
|
// as m_hwnd, and if not, it won't click. Cool!
|
||
|
if (ClickOnTheRect(&rcLoc,m_hwnd,FALSE))
|
||
|
return (S_OK);
|
||
|
else
|
||
|
return (E_NOT_APPLICABLE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopup::Clone()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopup::Clone(IEnumVARIANT **ppenum)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
hr = CreateMenuPopupClient(m_hwnd, m_idChildCur, IID_IEnumVARIANT,
|
||
|
(void**)ppenum);
|
||
|
if (SUCCEEDED(hr))
|
||
|
((CMenuPopup*)*ppenum)->SetParentInfo((IAccessible*)this,m_hMenu,m_ItemID);
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
STDMETHODIMP CMenuPopup::GetIdentityString (
|
||
|
DWORD dwIDChild,
|
||
|
BYTE ** ppIDString,
|
||
|
DWORD * pdwIDStringLen
|
||
|
)
|
||
|
{
|
||
|
*ppIDString = NULL;
|
||
|
*pdwIDStringLen = 0;
|
||
|
|
||
|
BYTE * pKeyData = (BYTE *) CoTaskMemAlloc( HMENUKEYSIZE );
|
||
|
if( ! pKeyData )
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
// Need to find pid of process that the menu belongs to. Can't use the
|
||
|
// pid of the popup menu, since that's a shared/reused system window.
|
||
|
// Instead, we assume that since the menu is present, it belongs to the
|
||
|
// current foreground thread, which is what GetGUIThreadInfo(NULL) gets us.
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
if( ! MyGetGUIThreadInfo( NULL, & GuiThreadInfo ) )
|
||
|
return E_FAIL;
|
||
|
DWORD dwPid = 0;
|
||
|
GetWindowThreadProcessId( GuiThreadInfo.hwndActive, & dwPid );
|
||
|
if( dwPid == 0 )
|
||
|
return E_FAIL;
|
||
|
|
||
|
MakeHmenuKey( pKeyData, dwPid, m_hMenu, dwIDChild );
|
||
|
|
||
|
*ppIDString = pKeyData;
|
||
|
*pdwIDStringLen = HMENUKEYSIZE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// ==========================================================================
|
||
|
//
|
||
|
// POPUP WINDOW FRAMES
|
||
|
//
|
||
|
// ==========================================================================
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CreateMenuPopupWindow()
|
||
|
//
|
||
|
// This creates a child object that represents the Window object for a
|
||
|
// popup menu. It has no members, but has one child (a cMenuPopup)
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
HRESULT CreateMenuPopupWindow(HWND hwnd, long idChildCur, REFIID riid, void** ppvMenuPopupW)
|
||
|
{
|
||
|
CMenuPopupFrame* pPopupFrame;
|
||
|
HRESULT hr;
|
||
|
|
||
|
InitPv(ppvMenuPopupW);
|
||
|
|
||
|
pPopupFrame = new CMenuPopupFrame(hwnd,idChildCur);
|
||
|
if (!pPopupFrame)
|
||
|
return(E_OUTOFMEMORY);
|
||
|
|
||
|
hr = pPopupFrame->QueryInterface(riid, ppvMenuPopupW);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
delete pPopupFrame;
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::CMenuPopupFrame()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuPopupFrame::CMenuPopupFrame(HWND hwnd,long idChildCur)
|
||
|
: CWindow( CLASS_MenuPopupWindow )
|
||
|
{
|
||
|
|
||
|
Initialize(hwnd, idChildCur);
|
||
|
m_hMenu = NULL;
|
||
|
m_ItemID = 0;
|
||
|
m_hwndParent = NULL;
|
||
|
m_fSonOfPopup = 0;
|
||
|
m_fSysMenu = 0;
|
||
|
|
||
|
m_hMenu = (HMENU)SendMessage (m_hwnd,MN_GETHMENU,0,0);
|
||
|
m_ItemID = FindItemIDThatOwnsThisMenu (m_hMenu,&m_hwndParent,
|
||
|
&m_fSonOfPopup,&m_fSysMenu);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::~CMenuPopupFrame()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
CMenuPopupFrame::~CMenuPopupFrame()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::SetupChildren()
|
||
|
//
|
||
|
// Frames have 1 child. That one child is the CMenuPopup.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
void CMenuPopupFrame::SetupChildren(void)
|
||
|
{
|
||
|
m_cChildren = 1;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::get_accParent()
|
||
|
//
|
||
|
// Parent of a popupmenuframe is the CMenuItem that created it (if any).
|
||
|
// To create one of those we need the grandparent. So we will create the
|
||
|
// grandparent (either a CMenuPopup, or a CMenu) temporarily, then we will
|
||
|
// create our parent CMenuItem based on that.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::get_accParent(IDispatch** ppdispParent)
|
||
|
{
|
||
|
IAccessible* paccGrandParent;
|
||
|
HRESULT hr;
|
||
|
CMenu* pMenu;
|
||
|
CMenuPopup* pMenuPopup;
|
||
|
|
||
|
InitPv(ppdispParent);
|
||
|
|
||
|
if (m_fSonOfPopup)
|
||
|
{
|
||
|
hr = CreateMenuPopupClient(m_hwndParent,0,IID_IAccessible,(void**)&paccGrandParent);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pMenuPopup = (CMenuPopup*)paccGrandParent;
|
||
|
hr = CreateMenuItem (paccGrandParent, // paccMenu IN pointer to the parent's IAccessible
|
||
|
m_hwndParent, // hwnd IN the hwnd of the window that owns the parent menu
|
||
|
pMenuPopup->GetMenu(), // hMenu IN the hmenu of the menu that owns this item.
|
||
|
m_hMenu, // hSubMenu IN the hMenu of the submenu this menu item opens
|
||
|
m_ItemID, // ItemID IN the menu item ID. Position (1..n).
|
||
|
0, // iCurChild IN ID of the current child in the enumeration
|
||
|
m_fSonOfPopup, // fPopup IN is this menu item in a popup or on a menu bar?
|
||
|
IID_IDispatch, // riid IN what interface are we asking for on this item?
|
||
|
(void**)ppdispParent); // ppvItem OUT the pointer to the interface asked for.
|
||
|
paccGrandParent->Release();
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
else if (m_fSysMenu)
|
||
|
{
|
||
|
hr = CreateSysMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccGrandParent);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pMenu = (CMenu*)paccGrandParent;
|
||
|
pMenu->SetupChildren();
|
||
|
hr = CreateMenuItem (paccGrandParent, // paccMenu IN pointer to the parent's IAccessible
|
||
|
m_hwndParent, // hwnd IN the hwnd of the window that owns the parent menu
|
||
|
pMenu->GetMenu(), // hMenu IN the hmenu of the menu that owns this item.
|
||
|
m_hMenu, // hSubMenu IN the hMenu of the submenu this menu item opens
|
||
|
m_ItemID, // ItemID IN the menu item ID. Position (1..n).
|
||
|
0, // iCurChild IN ID of the current child in the enumeration
|
||
|
m_fSonOfPopup, // fPopup IN is this menu item in a popup or on a menu bar?
|
||
|
IID_IDispatch, // riid IN what interface are we asking for on this item?
|
||
|
(void**)ppdispParent); // ppvItem OUT the pointer to the interface asked for.
|
||
|
paccGrandParent->Release();
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = CreateMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccGrandParent);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pMenu = (CMenu*)paccGrandParent;
|
||
|
pMenu->SetupChildren();
|
||
|
hr = CreateMenuItem (paccGrandParent, // paccMenu IN pointer to the parent's IAccessible
|
||
|
m_hwndParent, // hwnd IN the hwnd of the window that owns the parent menu
|
||
|
pMenu->GetMenu(), // hMenu IN the hmenu of the menu that owns this item.
|
||
|
m_hMenu, // hSubMenu IN the hMenu of the submenu this menu item opens
|
||
|
m_ItemID, // ItemID IN the menu item ID. Position (1..n).
|
||
|
0, // iCurChild IN ID of the current child in the enumeration
|
||
|
m_fSonOfPopup, // fPopup IN is this menu item in a popup or on a menu bar?
|
||
|
IID_IDispatch, // riid IN what interface are we asking for on this item?
|
||
|
(void**)ppdispParent); // ppvItem OUT the pointer to the interface asked for.
|
||
|
paccGrandParent->Release();
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
return (hr);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::get_accChild()
|
||
|
//
|
||
|
// What we want this do do is return (in ppdisp) an IDispatch pointer to
|
||
|
// the child specified by varChild. The 1 child of a CMenuPopupFrame is
|
||
|
// a cMenuPopup.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::get_accChild(VARIANT varChild, IDispatch** ppdisp)
|
||
|
{
|
||
|
InitPv(ppdisp);
|
||
|
|
||
|
if (!ValidateChild(&varChild) || varChild.lVal == CHILDID_SELF)
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
return (CreateMenuPopupClient(m_hwnd, 0,IID_IDispatch, (void**)ppdisp));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::get_accName
|
||
|
//
|
||
|
// Has very similar logic to CMenuPopup::get_accName
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::get_accName(VARIANT varChild, BSTR* pszName)
|
||
|
{
|
||
|
HWND hwndOwner;
|
||
|
TCHAR szClassName[50];
|
||
|
HRESULT hr;
|
||
|
IAccessible* paccParent;
|
||
|
|
||
|
InitPv(pszName);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (varChild.lVal == CHILDID_SELF)
|
||
|
{
|
||
|
// If we popped up from a menu bar or as another popup,
|
||
|
// then our name is the name of the thing that popped us
|
||
|
// up. If we are a floating popup, then our name is...?
|
||
|
//
|
||
|
// We implement this by either:
|
||
|
// 1. creating a parent object on the fly that we can
|
||
|
// ask the name of, OR
|
||
|
// 2. Looking for the name of the owner window, OR
|
||
|
// 3. Checking if we are the child of the start button.
|
||
|
// If all else fails, we'll just call ourselves "context menu".
|
||
|
if (m_hwndParent && m_ItemID)
|
||
|
{
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = m_ItemID;
|
||
|
|
||
|
if (m_fSonOfPopup)
|
||
|
hr = CreateMenuPopupClient(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
else if (m_fSysMenu)
|
||
|
hr = CreateSysMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
else
|
||
|
hr = CreateMenuBarObject(m_hwndParent,0,IID_IAccessible,(void**)&paccParent);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = paccParent->get_accName (varChild,pszName);
|
||
|
paccParent->Release();
|
||
|
}
|
||
|
return (hr);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Try to get the owner window and use that for a name
|
||
|
// This doesn't seem to work on anything I have ever found,
|
||
|
// but it should work if anything has an owner, so i'll
|
||
|
//leave it in. If it starts breaking, just rip it out.
|
||
|
if (m_hwnd)
|
||
|
{
|
||
|
IAccessible* pacc;
|
||
|
HRESULT hr;
|
||
|
|
||
|
hwndOwner = ::GetWindow (m_hwnd,GW_OWNER);
|
||
|
hr = AccessibleObjectFromWindow (hwndOwner, OBJID_WINDOW, IID_IAccessible, (void**)&pacc);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pacc->get_accName(varChild,pszName);
|
||
|
pacc->Release();
|
||
|
if (SUCCEEDED(hr))
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
// check if the start button has focus
|
||
|
hwndOwner = MyGetFocus();
|
||
|
if (InTheShell(hwndOwner, SHELL_TRAY))
|
||
|
{
|
||
|
GetClassName(hwndOwner,szClassName,ARRAYSIZE(szClassName));
|
||
|
if (lstrcmp(szClassName,TEXT("Button")) == 0)
|
||
|
return (HrCreateString(STR_STARTBUTTON,pszName));
|
||
|
}
|
||
|
// at least return this for a name
|
||
|
return (HrCreateString (STR_CONTEXT_MENU,pszName));
|
||
|
} // end else we don't have m_paccparent && m_itemid
|
||
|
} // end if childid_self
|
||
|
else
|
||
|
{
|
||
|
// not asking for name of the menupopupframe itself. We do not support asking for
|
||
|
// name of our child - have to talk to the child itself
|
||
|
return (E_INVALIDARG);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::accHitTest()
|
||
|
//
|
||
|
// We just need to return VARIANT with var.pDispVal set to be our one child,
|
||
|
// the CMenuPopup.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::accHitTest(long x, long y, VARIANT* pvarHit)
|
||
|
{
|
||
|
IDispatch* pdispChild;
|
||
|
HRESULT hr;
|
||
|
|
||
|
InitPvar(pvarHit);
|
||
|
SetupChildren();
|
||
|
|
||
|
pvarHit->vt = VT_I4;
|
||
|
pvarHit->lVal = CHILDID_SELF;
|
||
|
|
||
|
if (SendMessage(m_hwnd, WM_NCHITTEST, 0, MAKELONG(x, y)) == HTCLIENT)
|
||
|
{
|
||
|
hr = CreateMenuPopupClient (m_hwnd,0,IID_IDispatch,(void**)&pdispChild);
|
||
|
if (SUCCEEDED (hr))
|
||
|
{
|
||
|
pvarHit->vt = VT_DISPATCH;
|
||
|
pvarHit->pdispVal = pdispChild;
|
||
|
}
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
return(S_OK);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::get_accFocus()
|
||
|
//
|
||
|
// This fills in pvarFocus with the child that has the focus.
|
||
|
// Since we only have one child, We'll return an IDispatch to that child.
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::get_accFocus(VARIANT* pvarFocus)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
IDispatch* pdispChild;
|
||
|
|
||
|
InitPvar(pvarFocus);
|
||
|
hr = CreateMenuPopupClient(m_hwnd, 0,IID_IDispatch, (void**)&pdispChild);
|
||
|
if (!SUCCEEDED(hr))
|
||
|
return (hr);
|
||
|
|
||
|
pvarFocus->vt = VT_DISPATCH;
|
||
|
pvarFocus->pdispVal = pdispChild;
|
||
|
return (S_OK);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::accLocation()
|
||
|
//
|
||
|
// Location of Self and Child is the same.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
|
||
|
long* pcyHeight, VARIANT varChild)
|
||
|
{
|
||
|
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
|
||
|
|
||
|
if (!ValidateChild(&varChild))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
return(CWindow::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::accNavigate()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::accNavigate(long dwNavDir, VARIANT varStart, VARIANT* pvarEnd)
|
||
|
{
|
||
|
InitPvar(pvarEnd);
|
||
|
|
||
|
if (!ValidateChild(&varStart) ||
|
||
|
!ValidateNavDir(dwNavDir, varStart.lVal))
|
||
|
return(E_INVALIDARG);
|
||
|
|
||
|
if (dwNavDir == NAVDIR_FIRSTCHILD || dwNavDir == NAVDIR_LASTCHILD)
|
||
|
{
|
||
|
pvarEnd->vt = VT_DISPATCH;
|
||
|
return (CreateMenuPopupClient (m_hwnd,0,IID_IDispatch, (void**)&(pvarEnd->pdispVal)));
|
||
|
}
|
||
|
|
||
|
return (S_FALSE);
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::Clone()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::Clone(IEnumVARIANT **ppenum)
|
||
|
{
|
||
|
return (CreateMenuPopupWindow(m_hwnd, m_idChildCur, IID_IEnumVARIANT,
|
||
|
(void**)ppenum));
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// CMenuPopupFrame::Next()
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDMETHODIMP CMenuPopupFrame::Next(ULONG celt, VARIANT* rgvar, ULONG* pceltFetched)
|
||
|
{
|
||
|
VARIANT* pvar;
|
||
|
long cFetched;
|
||
|
|
||
|
// Can be NULL
|
||
|
if (pceltFetched)
|
||
|
*pceltFetched = 0;
|
||
|
|
||
|
pvar = rgvar;
|
||
|
cFetched = 0;
|
||
|
|
||
|
// we only have one child, so we can only return it if m_idChildCur == 0
|
||
|
if (m_idChildCur == 0)
|
||
|
{
|
||
|
cFetched++;
|
||
|
m_idChildCur++;
|
||
|
pvar->vt = VT_DISPATCH;
|
||
|
CreateMenuPopupClient (m_hwnd,0,IID_IDispatch, (void**)&(pvar->pdispVal));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Fill in the number fetched
|
||
|
//
|
||
|
if (pceltFetched)
|
||
|
*pceltFetched = cFetched;
|
||
|
|
||
|
//
|
||
|
// Return S_FALSE if we grabbed fewer items than requested
|
||
|
//
|
||
|
return((cFetched < (long)celt) ? S_FALSE : S_OK);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// This is a private function used to get the window handle that contains
|
||
|
// a given hSubMenu.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
HWND GetSubMenuWindow (HMENU hSubMenuToFind)
|
||
|
{
|
||
|
HWND hwndSubMenu;
|
||
|
BOOL bFound;
|
||
|
HMENU hSubMenuTemp;
|
||
|
|
||
|
hwndSubMenu = FindWindow (TEXT("#32768"),NULL);
|
||
|
if (hwndSubMenu == NULL)
|
||
|
return (NULL); // random error condition - shouldn't happen
|
||
|
|
||
|
if (!IsWindowVisible(hwndSubMenu))
|
||
|
return (NULL);
|
||
|
|
||
|
bFound = FALSE;
|
||
|
while (hwndSubMenu)
|
||
|
{
|
||
|
hSubMenuTemp = (HMENU)SendMessage (hwndSubMenu,MN_GETHMENU,0,0);
|
||
|
if (hSubMenuTemp == hSubMenuToFind)
|
||
|
{
|
||
|
bFound = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
hwndSubMenu = FindWindowEx (NULL,hwndSubMenu,TEXT("#32768"),NULL);
|
||
|
} // end while hwndSubMenu
|
||
|
|
||
|
if (bFound)
|
||
|
{
|
||
|
return(hwndSubMenu);
|
||
|
}
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
// This looks at each item in the Active window's menu and any other menu
|
||
|
// windows, until it finds one that has an hSubMenu that matches the hMenu
|
||
|
// we are trying to find. It then returns the ID of that thing (1..n) and
|
||
|
// fills in the window handle of the owner, and whether that window is a top
|
||
|
// level window or a popup menu.
|
||
|
// --------------------------------------------------------------------------
|
||
|
long FindItemIDThatOwnsThisMenu (HMENU hMenuOwned,HWND* phwndOwner,
|
||
|
BOOL* pfPopup,BOOL *pfSysMenu)
|
||
|
{
|
||
|
HWND hwndMenu;
|
||
|
HMENU hMenu;
|
||
|
int cItems;
|
||
|
int i;
|
||
|
|
||
|
if (IsBadWritePtr(phwndOwner,sizeof(HWND*)) ||
|
||
|
IsBadWritePtr (pfPopup,sizeof(BOOL*)) ||
|
||
|
IsBadWritePtr (pfSysMenu,sizeof(BOOL*)))
|
||
|
return 0;
|
||
|
|
||
|
*pfPopup = FALSE;
|
||
|
*pfSysMenu = FALSE;
|
||
|
*phwndOwner = NULL;
|
||
|
|
||
|
GUITHREADINFO GuiThreadInfo;
|
||
|
if( ! MyGetGUIThreadInfo (NULL,&GuiThreadInfo) )
|
||
|
return 0;
|
||
|
|
||
|
// check if it is from the sys menu first
|
||
|
MENUBARINFO mbi;
|
||
|
if( MyGetMenuBarInfo(GuiThreadInfo.hwndActive, OBJID_SYSMENU, 0, &mbi)
|
||
|
&& mbi.hMenu != NULL )
|
||
|
{
|
||
|
hMenu = mbi.hMenu;
|
||
|
|
||
|
if (GetSubMenu(hMenu,0) == hMenuOwned)
|
||
|
{
|
||
|
*pfSysMenu = TRUE;
|
||
|
*pfPopup = FALSE;
|
||
|
*phwndOwner = GuiThreadInfo.hwndActive;
|
||
|
return (1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if not from the sys menu, check the window's menu bar
|
||
|
hMenu = GetMenu (GuiThreadInfo.hwndActive);
|
||
|
if (hMenu)
|
||
|
{
|
||
|
cItems = GetMenuItemCount (hMenu);
|
||
|
for (i=0;i<cItems;i++)
|
||
|
{
|
||
|
#ifdef _DEBUG
|
||
|
if (!hMenu)
|
||
|
{
|
||
|
//DBPRINTF ("null hmenu at 5\r\n");
|
||
|
Assert (hMenu);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (GetSubMenu(hMenu,i) == hMenuOwned)
|
||
|
{
|
||
|
*pfPopup = FALSE;
|
||
|
*phwndOwner = GuiThreadInfo.hwndActive;
|
||
|
return (i+1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Okay, it doesn't belong to the active window's menu bar, maybe
|
||
|
// it belongs to a submenu of that...
|
||
|
hwndMenu = FindWindow (TEXT("#32768"),NULL);
|
||
|
while (hwndMenu)
|
||
|
{
|
||
|
hMenu = (HMENU)SendMessage (hwndMenu,MN_GETHMENU,0,0);
|
||
|
if (hMenu)
|
||
|
{
|
||
|
cItems = GetMenuItemCount (hMenu);
|
||
|
for (i=0;i<cItems;i++)
|
||
|
{
|
||
|
if (GetSubMenu(hMenu,i) == hMenuOwned)
|
||
|
{
|
||
|
*pfPopup = TRUE;
|
||
|
*phwndOwner = hwndMenu;
|
||
|
return (i+1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
hwndMenu = FindWindowEx (NULL,hwndMenu,TEXT("#32768"),NULL);
|
||
|
} // end while hwndMenu
|
||
|
|
||
|
// if we still haven't returned, then this menu is either a context
|
||
|
// menu, or belongs to the start button
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// MyGetMenuString()
|
||
|
//
|
||
|
// This tries to get the text of menu items. If they are ownerdraw, this
|
||
|
// will hack around in shell structures to get the text.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hwnd IN the hwnd that owns the menu
|
||
|
// hMenu IN the hMenu to talk to
|
||
|
// id IN the ID of the item to get (1..n)
|
||
|
// fShell IN TRUE if this is a shell owned menu - tells the function
|
||
|
// to hack into the shell's memory
|
||
|
// lpszBuf OUT gets filled in with the string
|
||
|
// cchMax IN number of characters in lpszBuf
|
||
|
// fAllowGenerated IN If TRUE, generated names (eg. those for MDI items)
|
||
|
// are allowed.
|
||
|
//
|
||
|
// Returns:
|
||
|
// TRUE if string was filled in, FALSE otherwise
|
||
|
//
|
||
|
// Returns:
|
||
|
// S_OK if string returned, S_FALSE for missing string, COM error code
|
||
|
// otherwise.
|
||
|
//
|
||
|
// 'Generated names' are menu item names that do not actually correspond
|
||
|
// to text on the menu item - eg. "Close"/"Restore" for the MDI buttons.
|
||
|
// Typically you do want this text if you are looking for the name, but you
|
||
|
// do not want this text if you are looking to extract a shortcut key.
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
BOOL MyGetMenuString( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, long id,
|
||
|
LPTSTR lpszBuf, UINT cchMax, BOOL fAllowGenerated )
|
||
|
{
|
||
|
--id;
|
||
|
*lpszBuf = 0;
|
||
|
|
||
|
MENUITEMINFO mii;
|
||
|
mii.cbSize = SIZEOF_MENUITEMINFO;
|
||
|
mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_DATA | MIIM_ID;
|
||
|
mii.dwTypeData = NULL;
|
||
|
mii.cch = 0;
|
||
|
|
||
|
if (!GetMenuItemInfo(hMenu, id, TRUE, &mii))
|
||
|
return FALSE;
|
||
|
|
||
|
// Is this a separator? If so, bail.
|
||
|
if (mii.fType & MFT_SEPARATOR)
|
||
|
return FALSE;
|
||
|
|
||
|
// For MDI windows - the min/restore/close buttons of the child window are
|
||
|
// actually menu items. Have to check for them here...
|
||
|
if( fAllowGenerated && hwnd && GetMDIMenuString( hwnd, hMenu, id, lpszBuf, cchMax ) )
|
||
|
return TRUE;
|
||
|
|
||
|
// If it's owner-draw, check if it supports the the 'dwData is ptr to
|
||
|
// MSAA data' workaround.
|
||
|
if( ( mii.fType & MFT_OWNERDRAW )
|
||
|
&& TryMSAAMenuHack( pTheObj, hwnd, mii.dwItemData, lpszBuf, cchMax ) )
|
||
|
return TRUE;
|
||
|
|
||
|
// Try to get the text.
|
||
|
//
|
||
|
// GetMenuString does the right thing for Win95/NT close/min/max
|
||
|
// system menu items - whereas mii.dwTypeData only contains a bitmap
|
||
|
// number (apparently - although cchSize does give the character
|
||
|
// count as though it were a string...)
|
||
|
if (GetMenuString(hMenu, id, lpszBuf, cchMax, MF_BYPOSITION))
|
||
|
return TRUE;
|
||
|
|
||
|
// is this a shell owner menu item?
|
||
|
// Check that (a) this is a shell menu, (b) it's ownerdraw, and
|
||
|
// (c) it has non-0 ownerdraw itemdata,
|
||
|
if ( InTheShell( hwnd, SHELL_PROCESS ) && ( mii.fType & MFT_OWNERDRAW ) && mii.dwItemData )
|
||
|
{
|
||
|
if( GetShellOwnerDrawMenu( hwnd, mii.dwItemData, lpszBuf, cchMax ) )
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// We've tried everything, but didn't get any name...
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetShellOwnerDrawMenu
|
||
|
//
|
||
|
// Pull text out of shell's internal data structs.
|
||
|
// This is used by the SendTo menu. (Also used to be used by the old
|
||
|
// pre-IE4 start menus, before they changed to being Toolbar32 based.)
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hwnd IN hwnd of menu
|
||
|
// dwItemData IN dwItemData from the menu
|
||
|
// lpszBuf IN/OUT gets filled in with the string
|
||
|
// cchMax in number of characters in lpszBuf
|
||
|
//
|
||
|
// Returns:
|
||
|
// TRUE if string was filled in, FALSE otherwise
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
BOOL GetShellOwnerDrawMenu( HWND hwnd, DWORD_PTR dwItemData, LPTSTR lpszBuf, UINT cchMax )
|
||
|
{
|
||
|
DWORD idProcess = 0;
|
||
|
GetWindowThreadProcessId(hwnd, &idProcess);
|
||
|
if (!idProcess)
|
||
|
return FALSE;
|
||
|
|
||
|
HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, idProcess);
|
||
|
if (!hProcess)
|
||
|
return FALSE;
|
||
|
|
||
|
//
|
||
|
// Try to read FILEMENUITEM's worth of stuff.
|
||
|
//
|
||
|
FILEMENUITEM fmi;
|
||
|
SIZE_T cbRead;
|
||
|
if (ReadProcessMemory(hProcess, (LPCVOID)dwItemData, &fmi, sizeof(fmi), &cbRead) &&
|
||
|
(cbRead == sizeof(fmi)))
|
||
|
{
|
||
|
//
|
||
|
// Is there a string pointer in here?
|
||
|
//
|
||
|
if (fmi.psz)
|
||
|
{
|
||
|
ReadProcessMemory(hProcess, fmi.psz, lpszBuf, cchMax, &cbRead);
|
||
|
lpszBuf[cchMax-1] = 0;
|
||
|
}
|
||
|
else if (fmi.pidl)
|
||
|
{
|
||
|
ITEMIDLIST id;
|
||
|
|
||
|
// No, we have to grovel inside of the PIDL
|
||
|
if (ReadProcessMemory(hProcess, fmi.pidl, &id, sizeof(ITEMIDLIST), &cbRead) &&
|
||
|
(cbRead == sizeof(id)))
|
||
|
{
|
||
|
id.cbTotal -= OFFSET_SZFRIENDLYNAME;
|
||
|
cchMax = min((DWORD)id.cbTotal, cchMax);
|
||
|
cchMax = max(cchMax, 1);
|
||
|
|
||
|
ReadProcessMemory(hProcess, (LPBYTE)fmi.pidl + OFFSET_SZFRIENDLYNAME,
|
||
|
lpszBuf, cchMax, &cbRead);
|
||
|
lpszBuf[cchMax-1] = 0;
|
||
|
|
||
|
//
|
||
|
// Are the last 4 characters ".lnk"? or ".pif"?
|
||
|
// or .??? - we'll cut 'em all off.
|
||
|
//
|
||
|
cchMax = lstrlen(lpszBuf);
|
||
|
if ((cchMax >= 4) && (lpszBuf[cchMax-4] == '.'))
|
||
|
lpszBuf[cchMax-4] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CloseHandle(hProcess);
|
||
|
|
||
|
return *lpszBuf != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMDIButtonIndex
|
||
|
//
|
||
|
// Returns appropritate INDEX_TITLEBAR_nnn for the given menu item, if it
|
||
|
// is really an MDI child button (restore/minimize/close).
|
||
|
//
|
||
|
// Returns 0 otherwise.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
UINT GetMDIButtonIndex( HMENU hMenu, DWORD idPos )
|
||
|
{
|
||
|
switch( GetMenuItemID( hMenu, idPos ) )
|
||
|
{
|
||
|
case SC_MINIMIZE: return INDEX_TITLEBAR_MINBUTTON;
|
||
|
case SC_RESTORE: return INDEX_TITLEBAR_RESTOREBUTTON;
|
||
|
case SC_CLOSE: return INDEX_TITLEBAR_CLOSEBUTTON;
|
||
|
default: return 0; // INDEX_TITLEBAR_SELF
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMDIChildMenuString
|
||
|
//
|
||
|
// Check if this is a MDI menu - return strings for the document menu,
|
||
|
// and the min/restore/close buttons
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hmenu IN handle of menu
|
||
|
// id IN 0-based index of the menu item
|
||
|
// lpszBuf IN/OUT gets filled in with the string
|
||
|
// cchMax in number of characters in lpszBuf
|
||
|
//
|
||
|
// Returns:
|
||
|
// TRUE if string was filled in, FALSE otherwise
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
BOOL GetMDIMenuString( HWND hwnd, HMENU hMenu, DWORD idPos, LPTSTR lpszBuf, UINT cchMax )
|
||
|
{
|
||
|
// For MDI windows - the min/restore/close buttons of the child window are
|
||
|
// actually owner-draw menu items. Have to check for them here...
|
||
|
|
||
|
UINT iIndex = GetMDIButtonIndex( hMenu, idPos );
|
||
|
if( iIndex )
|
||
|
{
|
||
|
return LoadString( hinstResDll, iIndex + STR_TITLEBAR_NAME, lpszBuf, cchMax ) != 0;
|
||
|
}
|
||
|
|
||
|
// Detect the document system menu by checking that it has a submenu
|
||
|
// which contains the Restore item (GetMenuState returns -1 if not found...)
|
||
|
// but which is not the actual system menu (since that also has a restore item).
|
||
|
HMENU hSub = GetSubMenu( hMenu, idPos );
|
||
|
if( hSub && GetMenuState( hSub, SC_RESTORE, MF_BYCOMMAND ) != -1
|
||
|
&& hSub != MyGetSystemMenu( hwnd ) )
|
||
|
{
|
||
|
return LoadString( hinstResDll, STR_DOCMENU_NAME, lpszBuf, cchMax ) != 0;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMDIMenuDescriptionString
|
||
|
//
|
||
|
// Check if this is a MDI menu - return description strings for the document
|
||
|
// menu, and the min/restore/close buttons
|
||
|
//
|
||
|
// Parameters:
|
||
|
// hmenu IN handle of menu
|
||
|
// idPos IN 0-based index of the menu item
|
||
|
// pbstr OUT returns the description of the item
|
||
|
//
|
||
|
// Returns:
|
||
|
// TRUE if item is a MDI element and pbstr was set, FALSE otherwise.
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
BOOL GetMDIMenuDescriptionString( HMENU hMenu, DWORD idPos, BSTR * pbstr )
|
||
|
{
|
||
|
UINT iIndex = GetMDIButtonIndex( hMenu, idPos );
|
||
|
if( iIndex )
|
||
|
{
|
||
|
return HrCreateString( iIndex + STR_TITLEBAR_DESCRIPTION, pbstr ) == S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// TryMSAAMenuHack()
|
||
|
//
|
||
|
// Checks if a menu supports the 'dwData is ptr to MSAA data' workaround.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// pTheObj IN the hwnd that owns the menu (used to get window handle
|
||
|
// if hWnd is NULL)
|
||
|
// hWnd IN hwnd of menu, NULL if not known (eg. invisible 'fake'
|
||
|
// popups)
|
||
|
// dwItemData IN dwItemData from the menu
|
||
|
// lpszBuf IN/OUT gets filled in with the string
|
||
|
// cchMax in number of characters in lpszBuf
|
||
|
//
|
||
|
// Returns:
|
||
|
// TRUE if string was filled in, FALSE otherwise
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
BOOL TryMSAAMenuHack( IAccessible * pTheObj,
|
||
|
HWND hWnd,
|
||
|
DWORD_PTR dwItemData,
|
||
|
LPTSTR lpszBuf,
|
||
|
UINT cchMax )
|
||
|
{
|
||
|
BOOL bGotIt = FALSE;
|
||
|
|
||
|
if( ! hWnd )
|
||
|
{
|
||
|
// It's an invisible 'fake' popup menu (CPopuMenu created to expose
|
||
|
// a HMENU, but no menu, and therefore no popup window, is currenly
|
||
|
// visible).
|
||
|
// Need a window handle so we can get the process id...
|
||
|
if( WindowFromAccessibleObjectEx( pTheObj, & hWnd ) != S_OK || hWnd == NULL )
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// ...now get process id...
|
||
|
DWORD idProcess = 0;
|
||
|
GetWindowThreadProcessId( hWnd, &idProcess );
|
||
|
if( !idProcess )
|
||
|
return FALSE;
|
||
|
|
||
|
// Open that process so we can read its memory...
|
||
|
HANDLE hProcess = OpenProcess( PROCESS_VM_READ, FALSE, idProcess );
|
||
|
if( hProcess )
|
||
|
{
|
||
|
// Treat dwItemData as an address, and try to read a
|
||
|
// MSAAMENUINFO struct from there...
|
||
|
MSAAMENUINFO menuinfo;
|
||
|
SIZE_T cbRead;
|
||
|
|
||
|
if( ReadProcessMemory( hProcess, (LPCVOID)dwItemData, (LPVOID) & menuinfo, sizeof( menuinfo ), &cbRead )
|
||
|
&& ( cbRead == sizeof( menuinfo ) ) )
|
||
|
{
|
||
|
|
||
|
// Check signature...
|
||
|
if( menuinfo.dwMSAASignature == MSAA_MENU_SIG )
|
||
|
{
|
||
|
// Work out len of UNICODE string to copy (+1 for terminating NUL)
|
||
|
DWORD copyLen = ( menuinfo.cchWText + 1 ) * sizeof( WCHAR );
|
||
|
|
||
|
WCHAR * pAlloc = (LPWSTR) LocalAlloc( LPTR, copyLen );
|
||
|
if( pAlloc )
|
||
|
{
|
||
|
|
||
|
// Do the copy... also fail if we read less than expected, or terminating NUL missing...
|
||
|
if( ReadProcessMemory( hProcess, (LPCVOID)menuinfo.pszWText, pAlloc, copyLen, &cbRead )
|
||
|
&& ( cbRead == copyLen )
|
||
|
&& ( pAlloc[ menuinfo.cchWText ] == '\0' ) )
|
||
|
{
|
||
|
|
||
|
#ifdef UNICODE
|
||
|
// Copy text to output buffer...
|
||
|
if( cchMax > 0 )
|
||
|
{
|
||
|
UINT cchCopy = menuinfo.cchWText;
|
||
|
if( cchCopy > cchMax - 1 )
|
||
|
cchCopy = cchMax - 1; // -1 for terminating NUL
|
||
|
memcpy( lpszBuf, pAlloc, cchCopy * sizeof( TCHAR ) );
|
||
|
lpszBuf[ cchCopy ] = L'\0';
|
||
|
bGotIt = TRUE;
|
||
|
}
|
||
|
#else
|
||
|
// Convert (and copy) UNICODE to ANSI...
|
||
|
if( WideCharToMultiByte( CP_ACP, 0, pAlloc, -1, lpszBuf, cchMax, NULL, NULL ) != 0 )
|
||
|
{
|
||
|
bGotIt = TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
LocalFree( pAlloc );
|
||
|
} // pAlloc
|
||
|
} // m_Signature
|
||
|
} // ReadProcessMemory
|
||
|
|
||
|
CloseHandle( hProcess );
|
||
|
} // hProcess
|
||
|
|
||
|
return bGotIt;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// WindowFromAccessibleObjectEx()
|
||
|
//
|
||
|
// This walks UP the ancestor chain until we find something who responds to
|
||
|
// IOleWindow(). Then we get the HWND from it.
|
||
|
//
|
||
|
// This is effectively a local version of WindowFromAccessibleObject
|
||
|
// This version doesn't stop till it runs out of objects, it gets a valid
|
||
|
// hwnd. The non-ex version stops even if it getgs a NULL hwnd.
|
||
|
// This allows us to navigate up through menupopups which have no hwnd
|
||
|
// (return NULL), but which do have parents, which eventually leads us to
|
||
|
// the owning hWnd.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
STDAPI WindowFromAccessibleObjectEx( IAccessible* pacc, HWND* phwnd )
|
||
|
{
|
||
|
IAccessible* paccT;
|
||
|
IOleWindow* polewnd;
|
||
|
IDispatch* pdispParent;
|
||
|
HRESULT hr;
|
||
|
|
||
|
//CWO: 12/4/96, Added check for NULL object
|
||
|
//CWO: 12/13/96, Removed NULL check, replaced with IsBadReadPtr check (#10342)
|
||
|
if (IsBadWritePtr(phwnd,sizeof(HWND*)) || IsBadReadPtr(pacc, sizeof(void*)))
|
||
|
return (E_INVALIDARG);
|
||
|
|
||
|
*phwnd = NULL;
|
||
|
paccT = pacc;
|
||
|
hr = S_OK;
|
||
|
|
||
|
while (paccT && SUCCEEDED(hr))
|
||
|
{
|
||
|
polewnd = NULL;
|
||
|
hr = paccT->QueryInterface(IID_IOleWindow, (void**)&polewnd);
|
||
|
if (SUCCEEDED(hr) && polewnd)
|
||
|
{
|
||
|
hr = polewnd->GetWindow(phwnd);
|
||
|
polewnd->Release();
|
||
|
|
||
|
// Don't quit if we just got a NULL hwnd...
|
||
|
// (this is the only change from WindowFromAccessibleObject(), which
|
||
|
// just unconditionally returned when it got here...)
|
||
|
if( *phwnd != NULL )
|
||
|
{
|
||
|
//
|
||
|
// Release an interface we obtained on our own, but not the one
|
||
|
// passed in.
|
||
|
//
|
||
|
if (paccT != pacc)
|
||
|
{
|
||
|
paccT->Release();
|
||
|
paccT = NULL;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get our parent.
|
||
|
//
|
||
|
pdispParent = NULL;
|
||
|
hr = paccT->get_accParent(&pdispParent);
|
||
|
|
||
|
//
|
||
|
// Release an interface we obtained on our own, but not the one
|
||
|
// passed in.
|
||
|
//
|
||
|
if (paccT != pacc)
|
||
|
{
|
||
|
paccT->Release();
|
||
|
}
|
||
|
|
||
|
paccT = NULL;
|
||
|
|
||
|
if (SUCCEEDED(hr) && pdispParent)
|
||
|
{
|
||
|
hr = pdispParent->QueryInterface(IID_IAccessible, (void**)&paccT);
|
||
|
pdispParent->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(hr);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMenuItemName()
|
||
|
//
|
||
|
// Returns the BSTR name for a menu item.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// pTheObj IN the hwnd that owns the menu (used to get window
|
||
|
// handle if hWnd is NULL)
|
||
|
// hWnd IN hwnd of menu, NULL if not known (eg. invisible
|
||
|
// 'fake' popups)
|
||
|
// hMenu IN Menu handle
|
||
|
// id IN 1-based id of the menu item (idChild)
|
||
|
// pszName OUT returns string containing text for menu item.
|
||
|
//
|
||
|
// Returns:
|
||
|
// S_OK if string returned, S_FALSE for missing string, COM error code
|
||
|
// otherwise.
|
||
|
//
|
||
|
// Catches special cases " " and "-", which get mapped to "System" and
|
||
|
// "Document window" respectively. These are the top-level and child window
|
||
|
// system menus.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
HRESULT GetMenuItemName( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id, BSTR * pszName )
|
||
|
{
|
||
|
Assert( hMenu );
|
||
|
|
||
|
TCHAR szItemName[256];
|
||
|
|
||
|
// TRUE -> allow generated names (eg. for MDI buttons)
|
||
|
if( MyGetMenuString( pTheObj, hwnd, hMenu, id, szItemName, ARRAYSIZE( szItemName ), TRUE ) )
|
||
|
{
|
||
|
StripMnemonic( szItemName );
|
||
|
|
||
|
if( lstrcmp( szItemName, TEXT(" ") ) == 0 )
|
||
|
{
|
||
|
return HrCreateString( STR_SYSMENU_NAME, pszName ); // "System"
|
||
|
}
|
||
|
|
||
|
if( lstrcmp( szItemName, TEXT("-") ) == 0 )
|
||
|
{
|
||
|
return HrCreateString( STR_DOCMENU_NAME, pszName ); // "Document window"
|
||
|
}
|
||
|
|
||
|
*pszName = TCharSysAllocString(szItemName);
|
||
|
if( ! *pszName )
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*szItemName = '\0';
|
||
|
*pszName = NULL;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMenuItemShortcut()
|
||
|
//
|
||
|
// Returns the BSTR name for a menu shortcut.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// pTheObj IN the hwnd that owns the menu (used to get window
|
||
|
// handle if hWnd is NULL)
|
||
|
// hWnd IN hwnd of menu, NULL if not known (eg. invisible
|
||
|
// 'fake' popups)
|
||
|
// hMenu IN Menu handle
|
||
|
// id IN 1-based id of the menu item (idChild)
|
||
|
// fIsMenuBar IN TRUE if the menu is a menubar; false for a popup
|
||
|
// pszShortcut OUT returns string containing kbshortcut for menu item.
|
||
|
//
|
||
|
// Returns:
|
||
|
// S_OK if string returned, S_FALSE for missing string, COM error code
|
||
|
// otherwise.
|
||
|
//
|
||
|
// Catches special case " " for system menu.
|
||
|
// If fMenuBar is TRUE, uses "Alt+%c" form.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
HRESULT GetMenuItemShortcut( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id,
|
||
|
BOOL fIsMenuBar, BSTR * pszShortcut )
|
||
|
{
|
||
|
TCHAR szHotKey[32];
|
||
|
szHotKey[0] = GetMenuItemHotkey( pTheObj, hwnd, hMenu, id,
|
||
|
GMIH_ALLOW_INITIAL | GMIH_ALLOW_SYS_SPACE );
|
||
|
szHotKey[1] = 0;
|
||
|
|
||
|
if ( szHotKey[0] == ' ' )
|
||
|
{
|
||
|
// Expand space character ' ' to the string "Space"
|
||
|
szHotKey[ 0 ] = '\0';
|
||
|
LoadString( hinstResDll, STR_SYSMENU_KEY, szHotKey, ARRAYSIZE( szHotKey ) );
|
||
|
}
|
||
|
|
||
|
// Fall through... This gives us c -> Alt+c if it was a single char,
|
||
|
// or ' ' -> "Space" -> "Alt+Space" if it was a space char (for sys menu).
|
||
|
|
||
|
if ( *szHotKey )
|
||
|
{
|
||
|
// If this is a menu bar, use the ALT+ form...
|
||
|
if ( fIsMenuBar )
|
||
|
{
|
||
|
// Make a string of the form "Alt+ch".
|
||
|
return HrMakeShortcut( szHotKey, pszShortcut );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// otherwise use just the key
|
||
|
*pszShortcut = TCharSysAllocString( szHotKey );
|
||
|
if ( ! *pszShortcut )
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pszShortcut = NULL;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// GetMenuItemHotkey()
|
||
|
//
|
||
|
// Returns the TCHAR hotkey for a menu, if one exists.
|
||
|
//
|
||
|
// Parameters:
|
||
|
// pTheObj IN the hwnd that owns the menu (used to get window
|
||
|
// handle if hWnd is NULL)
|
||
|
// hWnd IN hwnd of menu, NULL if not known (eg. invisible
|
||
|
// 'fake' popups)
|
||
|
// hMenu IN Menu handle
|
||
|
// id IN 1-based id of the menu item (idChild)
|
||
|
// fOption IN Option flags - see below.
|
||
|
//
|
||
|
// Returns:
|
||
|
// Hotkey character of menu item, or '\0' if item has no hotkey.
|
||
|
//
|
||
|
// Options:
|
||
|
//
|
||
|
// GMIH_ALLOW_INITIAL
|
||
|
// If set, allows the initial character of the menu item string to be
|
||
|
// returned as the shortcut key (provided that no other items also use
|
||
|
// that key as their menonic.)
|
||
|
//
|
||
|
// GMIH_ALLOW_SYS_SPACE = 0x02
|
||
|
// If set, returns ' ' as the shortcut key of the system menu item.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
TCHAR GetMenuItemHotkey( IAccessible * pTheObj, HWND hwnd, HMENU hMenu, LONG id, DWORD fOptions )
|
||
|
{
|
||
|
TCHAR szItemName[ 256 ];
|
||
|
|
||
|
// FALSE -> disallow generated names (eg. for MDI buttons)
|
||
|
if( ! MyGetMenuString( pTheObj, hwnd, hMenu, id, szItemName, ARRAYSIZE( szItemName ), FALSE ) )
|
||
|
{
|
||
|
return '\0';
|
||
|
}
|
||
|
|
||
|
// Check for menu name being " " - caller will want to treat this as a special hotkey of "Space".
|
||
|
// (eg. givein "Alt+Space" as the entire hotkey string.)
|
||
|
if( ( fOptions & GMIH_ALLOW_SYS_SPACE )
|
||
|
&& lstrcmp( szItemName, TEXT(" ") ) == 0 )
|
||
|
{
|
||
|
return ' ';
|
||
|
}
|
||
|
|
||
|
TCHAR ch = StripMnemonic( szItemName );
|
||
|
|
||
|
// Can stop here if caller doesn't want initial chars (ie. &-mnemonics only)
|
||
|
if( ! ( fOptions & GMIH_ALLOW_INITIAL ) )
|
||
|
{
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Did we get a hot-key? If so, use it.
|
||
|
if( ch != '\0' )
|
||
|
{
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
// Try initial letter instead...
|
||
|
LPTSTR pScanCh = szItemName;
|
||
|
while( *pScanCh == ' ' )
|
||
|
{
|
||
|
*pScanCh++;
|
||
|
}
|
||
|
|
||
|
// Obscure USER32 menu thing - was used to right-justify Help items in a previous version?
|
||
|
// Anyhow, USER skips over it to find the real first letter. So we do likewise...
|
||
|
if( *pScanCh == '\x08' )
|
||
|
{
|
||
|
pScanCh++;
|
||
|
}
|
||
|
|
||
|
// Just in case there's no initial letter... (all spaces)
|
||
|
if( *pScanCh == '\0' )
|
||
|
{
|
||
|
return '\0';
|
||
|
}
|
||
|
|
||
|
// Hotkeys are always returned as lowercase...
|
||
|
CharLowerBuff( pScanCh, 1 );
|
||
|
ch = *pScanCh;
|
||
|
|
||
|
|
||
|
|
||
|
// Now compare against all other menu items - if one other item has this
|
||
|
// initial letter as its mnemonic, then we can't use it for this one.
|
||
|
// (Mnemonic takes precedence over initial letters)
|
||
|
|
||
|
// GetMenuItemHotkey index is 1-based (ie. idChild), as is id, so also
|
||
|
// using a 1-based index here.
|
||
|
int cItems = GetMenuItemCount( hMenu );
|
||
|
for( int iScan = 1 ; iScan <= cItems ; iScan++ )
|
||
|
{
|
||
|
// Don't compare against this item!
|
||
|
if( iScan != id )
|
||
|
{
|
||
|
if( ch == GetMenuItemHotkey( pTheObj, hwnd, hMenu, iScan, NULL ) )
|
||
|
{
|
||
|
// Some other item has a mnemonic that is the same as this
|
||
|
// item's initial char - it mnemonic takes precedence, so
|
||
|
// this item has no kbshortcut.
|
||
|
return '\0';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No item use this item's initial char as a mnemonic - we're clear to use
|
||
|
// it as this item's kbshortcut.
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// INTERNAL
|
||
|
// MyGetSystemMenu()
|
||
|
//
|
||
|
// Returns the system HMENU for the given HWND.
|
||
|
//
|
||
|
// Can't use the Win32 API GetSystemMenu, since that modifies the system
|
||
|
// HMENU for the window.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
HMENU MyGetSystemMenu( HWND hwnd )
|
||
|
{
|
||
|
MENUBARINFO mbi;
|
||
|
if ( ! MyGetMenuBarInfo( hwnd, OBJID_SYSMENU, 0, &mbi ) )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
// GetMenuBarInfo returns a menu containing the sysmenu as its only
|
||
|
// submenu. Use GetSubMenu to access that...
|
||
|
return GetSubMenu( mbi.hMenu, 0 );
|
||
|
}
|