|
|
#include "shellprv.h"
// APPCOMPAT (lamadio): Conflicts with one defined in winuserp.h
#undef WINEVENT_VALID //It's tripping on this...
#include "winable.h"
#include "apithk.h"
#include "mnbandid.h"
#include "initguid.h"
#include "iaccess.h"
#include "mluisupp.h"
CAccessible::CAccessible(HMENU hmenu, WORD wID): _hMenu(hmenu), _wID(wID), _cRef(1) { _fState = MB_STATE_TRACK; }
CAccessible::CAccessible(IMenuBand* pmb): _cRef(1) { _fState = MB_STATE_MENU; _pmb = pmb; if (_pmb) { _pmb->AddRef(); } }
CAccessible::CAccessible(IMenuBand* pmb, int iIndex): _cRef(1) { _fState = MB_STATE_ITEM; _iAccIndex = iIndex; _pmb = pmb; if (_pmb) { _pmb->AddRef(); } }
CAccessible::~CAccessible() { ATOMICRELEASE(_pTypeInfo); ATOMICRELEASE(_pInnerAcc); switch (_fState) { case MB_STATE_TRACK: ASSERT(!_hwndMenuWindow || IsWindow(_hwndMenuWindow)); if (_hwndMenuWindow) { // Don't Destroy hmenu. It's part of a larger one...
SetMenu(_hwndMenuWindow, NULL); DestroyWindow(_hwndMenuWindow); _hwndMenuWindow = NULL; } break;
case MB_STATE_ITEM: ATOMICRELEASE(_pmtbItem); // Fall Through
case MB_STATE_MENU: ATOMICRELEASE(_pmtbTop); ATOMICRELEASE(_pmtbBottom); ATOMICRELEASE(_psma); ATOMICRELEASE(_pmb); break; } }
HRESULT CAccessible::InitAcc() { HRESULT hr = E_FAIL; if (_fInitialized) return NOERROR;
_fInitialized = TRUE; // We're initialized if we fail or not...
switch (_fState) { case MB_STATE_TRACK: if (EVAL(_hMenu)) { _hwndMenuWindow = CreateWindow(TEXT("static"), TEXT("MenuWindow"), WS_POPUP, 0, 0, 0, 0, NULL, _hMenu, g_hinst, NULL); if (EVAL(_hwndMenuWindow)) { IAccessible* paccChild1; hr = CreateStdAccessibleObject(_hwndMenuWindow, OBJID_MENU, IID_PPV_ARG(IAccessible, &paccChild1)); if (SUCCEEDED(hr)) { VARIANT varChild; varChild.vt = VT_I4; varChild.lVal = _wID + 1; //Accesibility is 1 based
// In order to get "On par" with the OleAcc's implementation of the HMENU wrapper,
// we need to do this twice. Once gets us the IAccessible for the "MenuItem" on the
// "Menubar". The second gets us the "Menuitem's" child. This is what we need to emulate
// their heirarchy.
IDispatch* pdispChild1; hr = paccChild1->get_accChild(varChild, &pdispChild1);
// OLEAcc returns a Success code (S_FALSE) while initializing the out param to zero.
// Explicitly test this situation.
// Does this have a Child?
if (hr == S_OK) { // Yes. Look for that child
IAccessible* paccChild2; hr = pdispChild1->QueryInterface(IID_PPV_ARG(IAccessible, &paccChild2));
// Does this have a child?
if (hr == S_OK) { // Yep, then we store this guy's child...
IDispatch* pdispChild2; varChild.lVal = 1; //Get the first child
hr = paccChild2->get_accChild(varChild, &pdispChild2); if (hr == S_OK) { hr = pdispChild2->QueryInterface(IID_PPV_ARG(IAccessible, &_pInnerAcc)); pdispChild2->Release(); } paccChild2->Release(); } pdispChild1->Release(); } paccChild1->Release(); } } } break;
case MB_STATE_ITEM: case MB_STATE_MENU: hr = _pmb->QueryInterface(IID_PPV_ARG(IShellMenuAcc, &_psma)); if (SUCCEEDED(hr)) { _psma->GetTop(&_pmtbTop); _psma->GetBottom(&_pmtbBottom); if (!_pmtbTop || !_pmtbBottom) { hr = E_FAIL; } }
if (SUCCEEDED(hr) && (_fState == MB_STATE_ITEM)) { VARIANT varChild; _GetVariantFromChildIndex(NULL, _iAccIndex, &varChild); if (SUCCEEDED(_GetChildFromVariant(&varChild, &_pmtbItem, &_iIndex))) _idCmd = GetButtonCmd(_pmtbItem->_hwndMB, _iIndex); }
break; }
return hr; }
/*----------------------------------------------------------
Purpose: IUnknown::AddRef method
*/ STDMETHODIMP_(ULONG) CAccessible::AddRef() { _cRef++; return _cRef; }
/*----------------------------------------------------------
Purpose: IUnknown::Release method
*/ STDMETHODIMP_(ULONG) CAccessible::Release() { ASSERT(_cRef > 0); _cRef--;
if (_cRef > 0) return _cRef;
delete this; return 0; }
/*----------------------------------------------------------
Purpose: IUnknown::QueryInterface method
*/ STDMETHODIMP CAccessible::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CAccessible, IDispatch), QITABENT(CAccessible, IAccessible), QITABENT(CAccessible, IEnumVARIANT), QITABENT(CAccessible, IOleWindow), { 0 }, };
return QISearch(this, (LPCQITAB)qit, riid, ppvObj); }
/*----------------------------------------------------------
Purpose: IDispatch::GetTypeInfoCount method
*/ STDMETHODIMP CAccessible::GetTypeInfoCount(UINT * pctinfo) { if (_pInnerAcc) return _pInnerAcc->GetTypeInfoCount(pctinfo); *pctinfo = 1; return NOERROR; }
/*----------------------------------------------------------
Purpose: IDispatch::GetTypeInfo method
*/ STDMETHODIMP CAccessible::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo ** pptinfo) { *pptinfo = NULL; if (_pInnerAcc) return _pInnerAcc->GetTypeInfo(itinfo, lcid, pptinfo);
if (itinfo != 0) return DISP_E_BADINDEX;
if (EVAL(_LoadTypeLib())) { *pptinfo = _pTypeInfo; _pTypeInfo->AddRef(); return NOERROR; } else return E_FAIL; }
/*----------------------------------------------------------
Purpose: IDispatch::GetIDsOfNames method
*/ STDMETHODIMP CAccessible::GetIDsOfNames(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { if (_pInnerAcc) return _pInnerAcc->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE;
if (EVAL(_LoadTypeLib())) { return _pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid); } else return E_FAIL; }
/*----------------------------------------------------------
Purpose: IDispatch::Invoke method
*/ STDMETHODIMP CAccessible::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
{ if (_pInnerAcc) return _pInnerAcc->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); if (IID_NULL != riid) return DISP_E_UNKNOWNINTERFACE;
if (EVAL(_LoadTypeLib())) { return _pTypeInfo->Invoke(static_cast<IDispatch*>(this), dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); } else return E_FAIL; }
BOOL CAccessible::_LoadTypeLib() { ITypeLib* pTypeLib; if (_pTypeInfo) return TRUE;
if (SUCCEEDED(LoadTypeLib(L"oleacc.dll", &pTypeLib))) { pTypeLib->GetTypeInfoOfGuid(IID_IAccessible, &_pTypeInfo); ATOMICRELEASE(pTypeLib); return TRUE; } return FALSE; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accParent method
*/ STDMETHODIMP CAccessible::get_accParent(IDispatch ** ppdispParent) { HRESULT hr = DISP_E_MEMBERNOTFOUND; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accParent(ppdispParent); break;
case MB_STATE_MENU: { IUnknown* punk; if (SUCCEEDED(_psma->GetParentSite(IID_PPV_ARG(IUnknown, &punk)))) { IAccessible* pacc; if (SUCCEEDED(IUnknown_QueryService(punk, SID_SMenuBandParent, IID_PPV_ARG(IAccessible, &pacc)))) { VARIANT varChild = {VT_I4, CHILDID_SELF}; // Init
hr = pacc->get_accFocus(&varChild); if (SUCCEEDED(hr)) { hr = pacc->get_accChild(varChild, ppdispParent); } VariantClear(&varChild); pacc->Release(); } else { // Another implementation headache: Accessibility requires
// us to return S_FALSE when there is no parent.
*ppdispParent = NULL; hr = S_FALSE; }
punk->Release(); }
return hr; } case MB_STATE_ITEM: // The parent of an item is the menuband itself
return IUnknown_QueryService(_psma, SID_SMenuPopup, IID_PPV_ARG(IDispatch, ppdispParent)); break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accChildCount method
*/ STDMETHODIMP CAccessible::get_accChildCount(long * pChildCount) { *pChildCount = 0; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accChildCount(pChildCount); break;
case MB_STATE_MENU: { int iTopCount = ToolBar_ButtonCount(_pmtbTop->_hwndMB); int iBottomCount = ToolBar_ButtonCount(_pmtbBottom->_hwndMB); *pChildCount = (_pmtbTop != _pmtbBottom)? iTopCount + iBottomCount : iTopCount; } break; case MB_STATE_ITEM: if (_pmtbItem->v_GetFlags(_idCmd) & SMIF_SUBMENU) *pChildCount = 1; break;
} return NOERROR; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accChild method
*/ STDMETHODIMP CAccessible::get_accChild(VARIANT varChildIndex, IDispatch ** ppdispChild) { HRESULT hr = DISP_E_MEMBERNOTFOUND; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accChild(varChildIndex, ppdispChild); break;
case MB_STATE_MENU: { if (varChildIndex.vt == VT_I4 && varChildIndex.lVal == CHILDID_SELF) { // So this is the ONLY menthod that is allowed to fail when something is
// unavailable.
*ppdispChild = NULL; hr = E_INVALIDARG; } else { int iIndex; // Since it's returing an index, we don't need to test the success case
_GetChildFromVariant(&varChildIndex, NULL, &iIndex); hr = _GetAccessibleItem(iIndex, ppdispChild); } } break;
case MB_STATE_ITEM: if (_pmtbItem->v_GetFlags(_idCmd) & SMIF_SUBMENU) { VARIANT varChild; hr = _GetVariantFromChildIndex(_pmtbItem->_hwndMB, _iIndex, &varChild); if (SUCCEEDED(hr)) { hr = _psma->GetSubMenu(&varChild, IID_PPV_ARG(IDispatch, ppdispChild)); if (FAILED(hr)) { hr = S_FALSE; } } } else hr = E_NOINTERFACE; break; } return hr; }
HRESULT CAccessible::_GetAccName(BSTR* pbstr) { IDispatch* pdisp; HRESULT hr = get_accParent(&pdisp); // Get parent can return a success code, but still fail to return a parent.
//
if (hr == S_OK) { IAccessible* pacc; hr = pdisp->QueryInterface(IID_PPV_ARG(IAccessible, &pacc)); if (SUCCEEDED(hr)) { VARIANT varChild; hr = pacc->get_accFocus(&varChild); if (SUCCEEDED(hr)) hr = pacc->get_accName(varChild, pbstr); } }
return hr; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accName method
*/ STDMETHODIMP CAccessible::get_accName(VARIANT varChild, BSTR* pszName) { CMenuToolbarBase* pmtb = _pmtbItem; int idCmd = _idCmd; int iIndex = _iIndex;
*pszName = NULL;
switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accName(varChild, pszName); break;
case MB_STATE_MENU: if (varChild.lVal == CHILDID_SELF) { if (_GetAccName(pszName) == S_FALSE) { TCHAR sz[100]; LoadString(HINST_THISDLL, IDS_ACC_APP, sz, ARRAYSIZE(sz)); *pszName = SysAllocStringT(sz); if (!*pszName) return E_OUTOFMEMORY; } return NOERROR; } else { if (FAILED(_GetChildFromVariant(&varChild, &pmtb, &iIndex))) return DISP_E_MEMBERNOTFOUND;
idCmd = GetButtonCmd(pmtb->_hwndMB, iIndex); }
// Fall Through
case MB_STATE_ITEM: { TCHAR sz[MAX_PATH]; int idString = 0; TBBUTTON tbb; if (ToolBar_GetButton(pmtb->_hwndMB, iIndex, &tbb) && tbb.fsStyle & BTNS_SEP) { idString = IDS_ACC_SEP; } else if (pmtb->GetChevronID() == _idCmd) { idString = IDS_ACC_CHEVRON; }
if (idString != 0) { LoadString(HINST_THISDLL, idString, sz, ARRAYSIZE(sz)); *pszName = SysAllocStringT(sz); } else { UINT cch = SendMessage(pmtb->_hwndMB, TB_GETBUTTONTEXT, idCmd, NULL);
if (cch != 0 && cch < ARRAYSIZE(sz)) { if (SendMessage(pmtb->_hwndMB, TB_GETBUTTONTEXT, idCmd, (LPARAM)sz) > 0) { SHStripMneumonic(sz); *pszName = SysAllocString(sz); } } }
if (_fState == MB_STATE_MENU) pmtb->Release();
if (!*pszName) return E_OUTOFMEMORY;
return NOERROR; } break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accValue method
*/ STDMETHODIMP CAccessible::get_accValue(VARIANT varChild, BSTR* pszValue) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accValue(varChild, pszValue); break;
case MB_STATE_MENU: case MB_STATE_ITEM: *pszValue = NULL; return S_FALSE; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accDescription method
*/ STDMETHODIMP CAccessible::get_accDescription(VARIANT varChild, BSTR * pszDescription) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accDescription(varChild, pszDescription); break;
case MB_STATE_MENU: if (FAILED(_GetAccName(pszDescription))) { TCHAR sz[100]; LoadString(HINST_THISDLL, IDS_ACC_APPMB, sz, ARRAYSIZE(sz)); *pszDescription = SysAllocStringT(sz); if (!*pszDescription) return E_OUTOFMEMORY; } break; case MB_STATE_ITEM: return get_accName(varChild, pszDescription); break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accRole method
*/ STDMETHODIMP CAccessible::get_accRole(VARIANT varChild, VARIANT *pvarRole) { pvarRole->vt = VT_I4; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accRole(varChild, pvarRole); break;
case MB_STATE_MENU: { BOOL fVertical, fOpen; _psma->GetState(&fVertical, &fOpen); pvarRole->lVal = fVertical ? ROLE_SYSTEM_MENUPOPUP : ROLE_SYSTEM_MENUBAR; return NOERROR; }
case MB_STATE_ITEM: pvarRole->lVal = ROLE_SYSTEM_MENUITEM; return NOERROR; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accState method
*/ STDMETHODIMP CAccessible::get_accState(VARIANT varChild, VARIANT *pvarState) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accState(varChild, pvarState); break;
case MB_STATE_MENU: { // All menus can be selected, and given focus. Most will be visible.
DWORD dwState = STATE_SYSTEM_FOCUSABLE;
BOOL fOpen, fVertical; _psma->GetState(&fVertical, &fOpen);
// Do we have a menu popped up?
if (fOpen) { // Yes, then we have focus
dwState |= STATE_SYSTEM_FOCUSED; } else if (fVertical) { // If we're a vertical menu without being popped up, then we're invisible.
dwState |= STATE_SYSTEM_INVISIBLE; }
pvarState->vt = VT_I4; pvarState->lVal = dwState; } return NOERROR;
case MB_STATE_ITEM: { DWORD dwState = 0;
TBBUTTON tbb; if (-1 != ToolBar_GetButton(_pmtbItem->_hwndMB, _iIndex, &tbb)) { dwState = tbb.fsState; // ToolBar_GetState returns -1 for some menus, need to use ToolBar_GetButton
}
int idHotTracked = ToolBar_GetHotItem(_pmtbItem->_hwndMB); DWORD dwAccState; if (dwState & TBSTATE_ENABLED) { dwAccState = STATE_SYSTEM_FOCUSABLE; } else { dwAccState = STATE_SYSTEM_UNAVAILABLE; }
if (dwState & (TBSTATE_PRESSED | TBSTATE_ENABLED)) { dwAccState |= STATE_SYSTEM_SELECTABLE | STATE_SYSTEM_FOCUSED; }
if ((-1 != idHotTracked) && (idHotTracked == _iIndex)) { dwAccState |= STATE_SYSTEM_HOTTRACKED; }
if (_pmtbItem->v_GetFlags(_idCmd) & SMIF_SUBMENU) { dwAccState |= STATE_SYSTEM_HASPOPUP; }
pvarState->vt = VT_I4; pvarState->lVal = dwAccState;
return NOERROR; } break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accHelp method
*/ STDMETHODIMP CAccessible::get_accHelp(VARIANT varChild, BSTR* pszHelp) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accHelp(varChild, pszHelp); break;
case MB_STATE_MENU: case MB_STATE_ITEM: // Not implemented
break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accHelpTopic method
*/ STDMETHODIMP CAccessible::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, long* pidTopic) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accHelpTopic(pszHelpFile, varChild, pidTopic); break;
case MB_STATE_MENU: case MB_STATE_ITEM: // Not implemented
break; } return DISP_E_MEMBERNOTFOUND; }
#define CH_PREFIX TEXT('&')
TCHAR GetAccelerator(LPCTSTR psz, BOOL bUseDefault) { TCHAR ch = (TCHAR)-1; LPCTSTR pszAccel = psz; // then prefixes are allowed.... see if it has one
do { pszAccel = StrChr(pszAccel, CH_PREFIX); if (pszAccel) { pszAccel = CharNext(pszAccel);
// handle having &&
if (*pszAccel != CH_PREFIX) ch = *pszAccel; else pszAccel = CharNext(pszAccel); } } while (pszAccel && (ch == (TCHAR)-1));
if ((ch == (TCHAR)-1) && bUseDefault) { // Since we're unicocde, we don't need to mess with MBCS
ch = *psz; }
return ch; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accKeyboardShortcut method
*/ STDMETHODIMP CAccessible::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszKeyboardShortcut) { CMenuToolbarBase* pmtb; int iIndex; HRESULT hr = DISP_E_MEMBERNOTFOUND; *pszKeyboardShortcut = NULL;
switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accKeyboardShortcut(varChild, pszKeyboardShortcut); break;
case MB_STATE_ITEM: pmtb = _pmtbItem; pmtb->AddRef(); iIndex = _iIndex; goto labelGetaccel;
case MB_STATE_MENU: { if (varChild.lVal != CHILDID_SELF) { if (SUCCEEDED(_GetChildFromVariant(&varChild, &pmtb, &iIndex))) { labelGetaccel: TCHAR szAccel[100] = TEXT(""); if (pmtb->_hwndMB) { TCHAR sz[MAX_PATH]; int idCmd = GetButtonCmd(pmtb->_hwndMB, iIndex); if (S_FALSE == _psma->IsEmpty()) { int cch = SendMessage(pmtb->_hwndMB, TB_GETBUTTONTEXT, idCmd, NULL); // cch is strlen of the string, not including terminator
if (cch != 0 && cch < ARRAYSIZE(sz)) { if (SendMessage(pmtb->_hwndMB, TB_GETBUTTONTEXT, idCmd, (LPARAM)sz) > 0) { BOOL fVertical, fOpen; _psma->GetState(&fVertical, &fOpen); if (!fVertical) { // minus 1 since we'll be adding a char.
LoadString(HINST_THISDLL, IDS_ACC_ALT, szAccel, ARRAYSIZE(szAccel) - 1); } int cchLen = lstrlen(szAccel); szAccel[cchLen] = GetAccelerator(sz, TRUE); szAccel[cchLen + 1] = TEXT('\0'); hr = S_OK; } } } }
if (SUCCEEDED(hr)) { *pszKeyboardShortcut = SysAllocStringT(szAccel); if (!*pszKeyboardShortcut) hr = E_OUTOFMEMORY; } pmtb->Release(); } } } break; }
return hr; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accFocus method
*/ STDMETHODIMP CAccessible::get_accFocus(VARIANT * pvarFocusChild) { HRESULT hr = DISP_E_MEMBERNOTFOUND; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accFocus(pvarFocusChild); break;
case MB_STATE_MENU: { pvarFocusChild->vt = VT_I4; pvarFocusChild->lVal = CHILDID_SELF;
CMenuToolbarBase* pmtbTracked; _psma->GetTracked(&pmtbTracked); if (pmtbTracked) { if (pmtbTracked->_hwndMB) { int iIndex = ToolBar_GetHotItem(pmtbTracked->_hwndMB); hr = _GetVariantFromChildIndex(pmtbTracked->_hwndMB, iIndex, pvarFocusChild); } pmtbTracked->Release(); } } break;
case MB_STATE_ITEM: // Not implemented;
break; } return hr; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accSelection method
*/ STDMETHODIMP CAccessible::get_accSelection(VARIANT * pvarSelectedChildren) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accSelection(pvarSelectedChildren); break;
case MB_STATE_MENU: return get_accFocus(pvarSelectedChildren); break;
case MB_STATE_ITEM: // Not implemented;
break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::get_accDefaultAction method
*/ STDMETHODIMP CAccessible::get_accDefaultAction(VARIANT varChild, BSTR* pszDefaultAction) { TCHAR sz[MAX_PATH];
switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->get_accDefaultAction(varChild, pszDefaultAction); break;
case MB_STATE_MENU: { LoadString(HINST_THISDLL, IDS_ACC_CLOSE, sz, ARRAYSIZE(sz)); *pszDefaultAction = SysAllocStringT(sz);
if (!*pszDefaultAction) return E_OUTOFMEMORY;
return NOERROR; }
case MB_STATE_ITEM: { if (S_OK == _psma->IsEmpty()) { sz[0] = TEXT('\0'); } else { int iId = (_pmtbItem->v_GetFlags(_idCmd) & SMIF_SUBMENU) ? IDS_ACC_OPEN: IDS_ACC_EXEC; LoadString(HINST_THISDLL, iId, sz, ARRAYSIZE(sz)); }
*pszDefaultAction = SysAllocStringT(sz); if (!*pszDefaultAction) return E_OUTOFMEMORY;
return NOERROR; } } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::accSelect method
*/ STDMETHODIMP CAccessible::accSelect(long flagsSelect, VARIANT varChild) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->accSelect(flagsSelect, varChild); break;
case MB_STATE_MENU: case MB_STATE_ITEM: break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::accLocation method
*/ STDMETHODIMP CAccessible::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) { CMenuToolbarBase* pmtb; int iIndex; HRESULT hr = DISP_E_MEMBERNOTFOUND; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild); break;
case MB_STATE_ITEM: pmtb = _pmtbItem; pmtb->AddRef(); iIndex = _iIndex; hr = NOERROR; goto labelGetRect;
case MB_STATE_MENU: { RECT rc; if (varChild.vt == VT_I4) { if (varChild.lVal == CHILDID_SELF) { IUnknown* punk; hr = _psma->GetParentSite(IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hr)) { IOleWindow* poct; hr = IUnknown_QueryService(punk, SID_SMenuPopup, IID_PPV_ARG(IOleWindow, &poct)); if (SUCCEEDED(hr)) { HWND hwnd; hr = poct->GetWindow(&hwnd); if (SUCCEEDED(hr)) { // Return the window rect of the menubar.
GetWindowRect(hwnd, &rc); }
poct->Release(); }
punk->Release(); } } else { hr = _GetChildFromVariant(&varChild, &pmtb, &iIndex); if (SUCCEEDED(hr)) { labelGetRect: if (pmtb->_hwndMB) { int idCmd = GetButtonCmd(pmtb->_hwndMB, iIndex); if (!ToolBar_GetRect(pmtb->_hwndMB, idCmd, &rc)) //1 based index
hr = E_INVALIDARG; MapWindowPoints(pmtb->_hwndMB, NULL, (LPPOINT)&rc, 2); } else { hr = E_FAIL; } pmtb->Release(); } }
if (SUCCEEDED(hr)) { *pxLeft = rc.left; *pyTop = rc.top; *pcxWidth = rc.right - rc.left; *pcyHeight = rc.bottom - rc.top; } } } break; } return hr; }
/*----------------------------------------------------------
Purpose: IAccessible::accNavigate method
*/ STDMETHODIMP CAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt) { HRESULT hr = DISP_E_MEMBERNOTFOUND; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->accNavigate(navDir, varStart, pvarEndUpAt); break;
case MB_STATE_MENU: return _Navigate(navDir, varStart, pvarEndUpAt); break;
case MB_STATE_ITEM: { VARIANT varChild; _GetVariantFromChildIndex(NULL, _iAccIndex, &varChild); return _Navigate(navDir, varChild, pvarEndUpAt); } } return hr; }
/*----------------------------------------------------------
Purpose: IAccessible::accHitTest method
*/ STDMETHODIMP CAccessible::accHitTest(long xLeft, long yTop, VARIANT * pvarChildAtPoint) { POINT pt = {xLeft, yTop}; switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->accHitTest(xLeft, yTop, pvarChildAtPoint); break;
case MB_STATE_MENU: { if (_psma) { int iIndex; HWND hwnd = WindowFromPoint(pt);
if (hwnd == _pmtbTop->_hwndMB || hwnd == _pmtbBottom->_hwndMB) { ScreenToClient(hwnd, &pt); iIndex = ToolBar_HitTest(hwnd, &pt); if (iIndex >= 0) { pvarChildAtPoint->vt = VT_DISPATCH; // This call expects the index to be an "Accessible" index which is one based
VARIANT varChild; _GetVariantFromChildIndex(hwnd, iIndex, &varChild);
//Since this is just returining an index, we don't need to test success
_GetChildFromVariant(&varChild, NULL, &iIndex); return _GetAccessibleItem(iIndex, &pvarChildAtPoint->pdispVal); } }
// Hmm, must be self
pvarChildAtPoint->vt = VT_I4; pvarChildAtPoint->lVal = CHILDID_SELF;
return S_OK; } } break;
case MB_STATE_ITEM: { RECT rc; MapWindowPoints(NULL, _pmtbItem->_hwndMB, &pt, 1);
if (ToolBar_GetRect(_pmtbItem->_hwndMB, _idCmd, &rc) && PtInRect(&rc, pt)) { pvarChildAtPoint->vt = VT_I4; pvarChildAtPoint->lVal = CHILDID_SELF; } else { pvarChildAtPoint->vt = VT_EMPTY; pvarChildAtPoint->lVal = (DWORD)(-1); } return NOERROR; } break;
} return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::accDoDefaultAction method
*/ STDMETHODIMP CAccessible::accDoDefaultAction(VARIANT varChild) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->accDoDefaultAction(varChild); break;
case MB_STATE_MENU: if (_psma) return _psma->DoDefaultAction(&varChild); break;
case MB_STATE_ITEM: if (SendMessage(_pmtbItem->_hwndMB, TB_SETHOTITEM2, _iIndex, HICF_OTHER | HICF_RESELECT | HICF_TOGGLEDROPDOWN)) return NOERROR; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::put_accName method
*/ STDMETHODIMP CAccessible::put_accName(VARIANT varChild, BSTR szName) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->put_accName(varChild, szName); break; } return DISP_E_MEMBERNOTFOUND; }
/*----------------------------------------------------------
Purpose: IAccessible::put_accValue method
*/ STDMETHODIMP CAccessible::put_accValue(VARIANT varChild, BSTR pszValue) { switch (_fState) { case MB_STATE_TRACK: if (_pInnerAcc) return _pInnerAcc->put_accValue(varChild, pszValue); break; } return DISP_E_MEMBERNOTFOUND; }
HRESULT CAccessible::_Navigate(long navDir, VARIANT varStart, VARIANT * pvarEndUpAt) { ASSERT(pvarEndUpAt); int iTBIndex; HRESULT hr = S_FALSE;
TBBUTTONINFO tbInfo; tbInfo.cbSize = sizeof(TBBUTTONINFO);
pvarEndUpAt->vt = VT_DISPATCH; pvarEndUpAt->pdispVal = NULL;
int iIndex = 0; // 1 based index
_GetChildFromVariant(&varStart, NULL, &iIndex);
BOOL fVertical; BOOL fOpen; _psma->GetState(&fVertical, &fOpen); if (!fVertical) { static const long navMap[] = { NAVDIR_LEFT, // Map to Up
NAVDIR_RIGHT, // Map to Down
NAVDIR_UP, // Map to Left
NAVDIR_DOWN, // Map to Right
}; // uhhhh what?
if (IsInRange(navDir, NAVDIR_UP, NAVDIR_RIGHT)) navDir = navMap[navDir - NAVDIR_UP]; }
switch (navDir) { case NAVDIR_NEXT: { VARIANT varVert = {0}; // For the Vertical case, Next should return an error.
// Is this band vertical?
// Don't do this for anything but the menu case.
if (_fState == MB_STATE_MENU && SUCCEEDED(IUnknown_QueryServiceExec(_psma, SID_SMenuBandParent, &CGID_MenuBand, MBANDCID_ISVERTICAL, 0, NULL, &varVert)) && varVert.boolVal == VARIANT_TRUE) { ASSERT(VT_BOOL == varVert.vt); // Yes. Then punt
hr = S_FALSE; break; } // Fall Through
} //Fall through
case NAVDIR_DOWN: hr = NOERROR; while (SUCCEEDED(hr)) { iIndex++; VARIANT varTemp; hr = _GetVariantFromChildIndex(NULL, iIndex, &varTemp); if (SUCCEEDED(hr)) { CMenuToolbarBase *pmtb; hr = _GetChildFromVariant(&varTemp, &pmtb, &iTBIndex); if (SUCCEEDED(hr)) { if (pmtb->_hwndMB) { tbInfo.dwMask = TBIF_STATE | TBIF_STYLE; int idCmd = GetButtonCmd(pmtb->_hwndMB, iTBIndex); ToolBar_GetButtonInfo(pmtb->_hwndMB, idCmd, &tbInfo); } else { hr = E_FAIL; } pmtb->Release();
if (!(tbInfo.fsState & TBSTATE_HIDDEN) && !(tbInfo.fsStyle & TBSTYLE_SEP)) { break; } } } } break;
case NAVDIR_FIRSTCHILD: if (_fState == MB_STATE_ITEM) { pvarEndUpAt->vt = VT_EMPTY; pvarEndUpAt->pdispVal = NULL; hr = S_FALSE; break; }
iIndex = 0; hr = NOERROR; break;
case NAVDIR_LASTCHILD: if (_fState == MB_STATE_ITEM) { pvarEndUpAt->vt = VT_EMPTY; pvarEndUpAt->pdispVal = NULL; hr = S_FALSE; break; } iIndex = -1; hr = NOERROR; break;
case NAVDIR_LEFT: pvarEndUpAt->vt = VT_DISPATCH; return get_accParent(&pvarEndUpAt->pdispVal); break;
case NAVDIR_RIGHT: { CMenuToolbarBase* pmtb = (varStart.lVal & TOOLBAR_MASK)? _pmtbTop : _pmtbBottom; if (pmtb->_hwndMB) { int idCmd = GetButtonCmd(pmtb->_hwndMB, (varStart.lVal & ~TOOLBAR_MASK) - 1); if (pmtb->v_GetFlags(idCmd) & SMIF_SUBMENU) { IMenuPopup* pmp; hr = _psma->GetSubMenu(&varStart, IID_PPV_ARG(IMenuPopup, &pmp)); if (SUCCEEDED(hr)) { IAccessible* pacc; hr = IUnknown_QueryService(pmp, SID_SMenuBandChild, IID_PPV_ARG(IAccessible, &pacc)); if (SUCCEEDED(hr)) { hr = pacc->accNavigate(NAVDIR_FIRSTCHILD, varStart, pvarEndUpAt); pacc->Release(); } pmp->Release(); } } } else { hr = E_FAIL; }
return hr; } break;
case NAVDIR_PREVIOUS: { VARIANT varVert = {0}; // For the Vertical case, Pervious should return an error.
// Is this band vertical?
// Don't do this for anything but the menu case.
if (_fState == MB_STATE_MENU && SUCCEEDED(IUnknown_QueryServiceExec(_psma, SID_SMenuBandParent, &CGID_MenuBand, MBANDCID_ISVERTICAL, 0, NULL, &varVert)) && varVert.boolVal == VARIANT_TRUE) { ASSERT(VT_BOOL == varVert.vt); // Yes. Then punt
hr = S_FALSE; break; } // Fall Through
} //Fall through
case NAVDIR_UP: hr = NOERROR; while (SUCCEEDED(hr)) { iIndex--; VARIANT varTemp; hr = _GetVariantFromChildIndex(NULL, iIndex, &varTemp); if (SUCCEEDED(hr)) { CMenuToolbarBase *pmtb; hr = _GetChildFromVariant(&varTemp, &pmtb, &iTBIndex); if (SUCCEEDED(hr)) { if (iTBIndex == 0) { hr = S_FALSE; //Don't navigate to self, allow the top bar to get a whack.
IUnknown* punk; if (SUCCEEDED(_psma->GetParentSite(IID_PPV_ARG(IUnknown, &punk)))) { IOleCommandTarget* poct; if (SUCCEEDED(IUnknown_QueryService(punk, SID_SMenuBandParent, IID_PPV_ARG(IOleCommandTarget, &poct)))) { VARIANT varVert = {0};
if (SUCCEEDED(poct->Exec(&CGID_MenuBand, MBANDCID_ISVERTICAL, 0, NULL, &varVert)) && varVert.boolVal == VARIANT_FALSE) { ASSERT(VT_BOOL == varVert.vt);
IAccessible* pacc; if (SUCCEEDED(IUnknown_QueryService(punk, SID_SMenuBandParent, IID_PPV_ARG(IAccessible, &pacc)))) { VARIANT varChild = {VT_I4, CHILDID_SELF}; hr = pacc->get_accFocus(&varChild); if (SUCCEEDED(hr)) { hr = pacc->get_accChild(varChild, &pvarEndUpAt->pdispVal); }
VariantClear(&varChild); pacc->Release(); } } poct->Release(); } punk->Release(); } } // iTBIndex == 0
tbInfo.dwMask = TBIF_STATE | TBIF_STYLE; if (pmtb->_hwndMB) { int idCmd = GetButtonCmd(pmtb->_hwndMB, iTBIndex); ToolBar_GetButtonInfo(pmtb->_hwndMB, idCmd, &tbInfo); } else { hr = E_FAIL; } pmtb->Release();
if (!(tbInfo.fsState & TBSTATE_HIDDEN) && !(tbInfo.fsStyle & TBSTYLE_SEP)) { break; } } } } break;
default: hr = E_INVALIDARG; }
if (SUCCEEDED(hr) && S_FALSE != hr) hr = _GetAccessibleItem(iIndex, &pvarEndUpAt->pdispVal);
return hr; }
HRESULT CAccessible::_GetVariantFromChildIndex(HWND hwnd, int iIndex, VARIANT* pvarChild) { // First bit: Top 1, bottom 0
// Rest is index into that toolbar.
pvarChild->vt = VT_I4; pvarChild->lVal = iIndex + 1;
if (hwnd) { if (hwnd == _pmtbTop->_hwndMB) { pvarChild->lVal |= TOOLBAR_MASK; } } else { // Caller wants us to figure out based on index from top.
int iTopCount = ToolBar_ButtonCount(_pmtbTop->_hwndMB); int iBottomCount = ToolBar_ButtonCount(_pmtbBottom->_hwndMB); int iTotalCount = (_pmtbTop != _pmtbBottom)? iTopCount + iBottomCount : iTopCount;
if (iIndex < iTopCount) { pvarChild->lVal |= TOOLBAR_MASK; } else { pvarChild->lVal -= iTopCount; }
// This works because:
// If there are 2 toolbars, the bottom one is represented by top bit clear.
// If there is only one, then it doesn't matter if it's top or bottom.
// lVal is not zero based....
if (iIndex == -1) pvarChild->lVal = iTotalCount;
if (iIndex >= iTotalCount) return E_FAIL; }
return NOERROR; }
HRESULT CAccessible::_GetChildFromVariant(VARIANT* pvarChild, CMenuToolbarBase** ppmtb, int* piIndex) { ASSERT(_pmtbTop && _pmtbBottom); ASSERT(piIndex);
if (ppmtb) *ppmtb = NULL;
*piIndex = -1;
// Passing a NULL for an HWND returns the index from the beginning of the set.
int iAdd = 0; if (pvarChild->vt != VT_I4) return E_FAIL;
if (pvarChild->lVal & TOOLBAR_MASK) { if (ppmtb) { *ppmtb = _pmtbTop; } } else { if (ppmtb) { *ppmtb = _pmtbBottom; } else { iAdd = ToolBar_ButtonCount(_pmtbTop->_hwndMB); } }
if (ppmtb && *ppmtb) (*ppmtb)->AddRef();
*piIndex = (pvarChild->lVal & ~TOOLBAR_MASK) + iAdd - 1;
return (ppmtb && !*ppmtb) ? E_FAIL : S_OK; }
HRESULT CAccessible::_GetAccessibleItem(int iIndex, IDispatch** ppdisp) { HRESULT hr = E_OUTOFMEMORY; CAccessible* pacc = new CAccessible(_pmb, iIndex);
if (pacc) { if (SUCCEEDED(pacc->InitAcc())) { hr = pacc->QueryInterface(IID_PPV_ARG(IDispatch, ppdisp)); } pacc->Release(); } return hr; }
// *** IEnumVARIANT methods ***
STDMETHODIMP CAccessible::Next(unsigned long celt, VARIANT * rgvar, unsigned long * pceltFetched) {
// Picky customer complaint. Check for NULL...
if (pceltFetched) *pceltFetched = 1; return _GetVariantFromChildIndex(NULL, _iEnumIndex++, rgvar); }
STDMETHODIMP CAccessible::Skip(unsigned long celt) { return E_NOTIMPL; }
STDMETHODIMP CAccessible::Reset() { _iEnumIndex = 0; return NOERROR; }
STDMETHODIMP CAccessible::Clone(IEnumVARIANT ** ppenum) { return E_NOTIMPL; }
// *** IOleWindow methods ***
STDMETHODIMP CAccessible::GetWindow(HWND * lphwnd) { *lphwnd = NULL;
switch (_fState) { case MB_STATE_TRACK: *lphwnd = _hwndMenuWindow; break;
case MB_STATE_ITEM: *lphwnd = _pmtbItem->_hwndMB; break;
case MB_STATE_MENU: *lphwnd = _pmtbTop->_hwndMB; break; }
if (*lphwnd) return NOERROR;
return E_FAIL; }
STDMETHODIMP CAccessible::ContextSensitiveHelp(BOOL fEnterMode) { return E_NOTIMPL; }
|