windows-nt/Source/XPSP1/NT/windows/richedit/re30/iaccess.cpp
2020-09-26 16:20:57 +08:00

2396 lines
66 KiB
C++

/*
* IACCESS.CPP
*
* Purpose:
* Implemenation of IAccessibility for listbox and combobox
*
* Original Author:
* Jerry Kim
*
* History: <nl>
* 01/04/99 - v-jerrki Created
*
* Set tabs every four (4) columns
*
* Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved.
*/
#include "_common.h"
#include "_host.h"
#include "_cbhost.h"
#ifndef NOACCESSIBILITY
extern LRESULT CALLBACK RichListBoxWndProc(HWND, UINT, WPARAM, LPARAM);
#define InitPv(pv) *pv = NULL
#define InitPlong(plong) *plong = 0
#define InitPvar(pvar) pvar->vt = VT_EMPTY
#define ValidateFlags(flags, valid) (!((flags) & ~(valid)))
#define InitAccLocation(px, py, pcx, pcy) {InitPlong(px); InitPlong(py); InitPlong(pcx); InitPlong(pcy);}
#ifdef _WIN64
#define HwndFromHWNDID(lId) (HWND)((DWORD_PTR)(lId) & ~0x80000000)
#else
#define HwndFromHWNDID(lId) (HWND)((lId) & ~0x80000000)
#endif // _WIN64
// this is for ClickOnTheRect
typedef struct tagMOUSEINFO
{
int MouseThresh1;
int MouseThresh2;
int MouseSpeed;
}
MOUSEINFO, FAR* LPMOUSEINFO;
#define IsHWNDID(lId) ((lId) & 0x80000000)
//////////////////////// Accessibility Utility Functions ///////////////////////////
namespace MSAA
{
// --------------------------------------------------------------------------
//
// InitTypeInfo()
//
// This initializes our type info when we need it for IDispatch junk.
//
// --------------------------------------------------------------------------
HRESULT InitTypeInfo(ITypeInfo** ppiTypeInfo)
{
Assert(ppiTypeInfo);
if (*ppiTypeInfo)
return S_OK;
// Try getting the typelib from the registry
ITypeLib *piTypeLib;
HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, 0, &piTypeLib);
if (FAILED(hr))
hr = LoadTypeLib(OLESTR("OLEACC.DLL"), &piTypeLib);
if (SUCCEEDED(hr))
{
hr = piTypeLib->GetTypeInfoOfGuid(IID_IAccessible, ppiTypeInfo);
piTypeLib->Release();
if (!SUCCEEDED(hr))
*ppiTypeInfo = NULL;
}
return(hr);
}
// --------------------------------------------------------------------------
//
// ValidateChild()
//
// --------------------------------------------------------------------------
BOOL ValidateChild(VARIANT *pvar, int ctChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "ValidateChild");
// Missing parameter, a la VBA
TryAgain:
switch (pvar->vt)
{
case VT_VARIANT | VT_BYREF:
W32->VariantCopy(pvar, pvar->pvarVal);
goto TryAgain;
case VT_ERROR:
if (pvar->scode != DISP_E_PARAMNOTFOUND)
return(FALSE);
// FALL THRU
case VT_EMPTY:
pvar->vt = VT_I4;
pvar->lVal = 0;
break;
case VT_I4:
if ((pvar->lVal < 0) || (pvar->lVal > ctChild))
return(FALSE);
break;
default:
return(FALSE);
}
return(TRUE);
}
// --------------------------------------------------------------------------
//
// ValidateSelFlags()
//
// Validates selection flags.
// this makes sure the only bits set are in the valid range and that you don't
// have any invalid combinations.
// Invalid combinations are
// ADDSELECTION and REMOVESELECTION
// ADDSELECTION and TAKESELECTION
// REMOVESELECTION and TAKESELECTION
// EXTENDSELECTION and TAKESELECTION
//
// --------------------------------------------------------------------------
BOOL ValidateSelFlags(long flags)
{
if (!ValidateFlags((flags), SELFLAG_VALID))
return (FALSE);
if ((flags & SELFLAG_ADDSELECTION) &&
(flags & SELFLAG_REMOVESELECTION))
return FALSE;
if ((flags & SELFLAG_ADDSELECTION) &&
(flags & SELFLAG_TAKESELECTION))
return FALSE;
if ((flags & SELFLAG_REMOVESELECTION) &&
(flags & SELFLAG_TAKESELECTION))
return FALSE;
if ((flags & SELFLAG_EXTENDSELECTION) &&
(flags & SELFLAG_TAKESELECTION))
return FALSE;
return TRUE;
}
// --------------------------------------------------------------------------
//
// GetStringResource(UINT id, WCHAR* psz, int nSize)
//
// Gets the string resource for a given id and puts it in the passed buffer
//
// --------------------------------------------------------------------------
HRESULT GetStringResource(UINT id, BSTR* pbstr)
{
WCHAR sz[MAX_PATH] = L"\0";
if (!pbstr)
return S_FALSE;
/*
// UNDONE:
// Need a workaround for this localization issue
if (Win9x())
{
if (!LoadStringA(hinstResDll, id, sz, MAX_PATH))
return(E_OUTOFMEMORY);
// On Win9x we get ansi so convert it
int cchUText = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)sz, -1, NULL, 0) + 1;
*pbstr = SysAllocStringLen(NULL, cchUText);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)psz, -1, *pbstr, cchUText);
}
else
{
if (!LoadStringW(hinstResDll, id, sz, MAX_PATH))
return(E_OUTOFMEMORY);
*pbstr = SysAllocString(sz);
}
*/
#define STR_DOUBLE_CLICK 1
#define STR_DROPDOWN_HIDE 2
#define STR_DROPDOWN_SHOW 3
#define STR_ALT 4
#define STR_COMBOBOX_LIST_SHORTCUT 5
switch (id)
{
case STR_DOUBLE_CLICK:
//"Double Click"
wcscpy(sz, L"Double Click");
break;
case STR_DROPDOWN_HIDE:
//"Hide"
wcscpy(sz, L"Hide");
break;
case STR_DROPDOWN_SHOW:
//"Show"
wcscpy(sz, L"Show");
break;
case STR_ALT:
//"Alt+"
wcscpy(sz, L"Alt+");
break;
case STR_COMBOBOX_LIST_SHORTCUT:
//"Alt+Down Arrow"
wcscpy(sz, L"Alt+Down Arrow");
break;
default:
AssertSz(FALSE, "id not found!!");
}
*pbstr = SysAllocString(sz);
if (!*pbstr)
return(E_OUTOFMEMORY);
return(S_OK);
}
// --------------------------------------------------------------------------
//
// HWND GetAncestor(HWND hwnd, UINT gaFlags)
//
// This gets the ancestor window where
// GA_PARENT gets the "real" parent window
// GA_ROOT gets the "real" top level parent window (not inc. owner)r
//
// * The _real_ parent. This does NOT include the owner, unlike
// GetParent(). Stops at a top level window unless we start with
// the desktop. In which case, we return the desktop.
// * The _real_ root, caused by walking up the chain getting the
// ancestor.
//
// NOTE:
// User32.exe provides a undocumented function similar to this but
// it doesn't exist in NT4. Also, GA_ROOT works differently on Win98 so
// I copied this over from msaa
// --------------------------------------------------------------------------
HWND GetAncestor(HWND hwnd, UINT gaFlags)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetAncestor");
HWND hwndDesktop = GetDesktopWindow();
if (hwnd == hwndDesktop || !::IsWindow(hwnd))
return(NULL);
DWORD dwStyle = GetWindowLong (hwnd, GWL_STYLE);
HWND hwndParent;
switch (gaFlags)
{
case GA_PARENT:
if (dwStyle & WS_CHILD)
hwndParent = GetParent(hwnd);
else
hwndParent = GetWindow(hwnd, GW_OWNER);
hwnd = hwndParent;
break;
case GA_ROOT:
if (dwStyle & WS_CHILD)
hwndParent = GetParent(hwnd);
else
hwndParent = GetWindow(hwnd, GW_OWNER);
while (hwndParent != hwndDesktop && hwndParent != NULL)
{
hwnd = hwndParent;
dwStyle = GetWindowLong(hwnd, GWL_STYLE);
if (dwStyle & WS_CHILD)
hwndParent = GetParent(hwnd);
else
hwndParent = GetWindow(hwnd, GW_OWNER);
}
break;
default:
AssertSz(FALSE, "Invalid flag");
}
return(hwnd);
}
// --------------------------------------------------------------------------
//
// GetTextString(HWND hwnd, BSTR* bstr)
//
// Parameters: hwnd of the window to get the text from
//
// --------------------------------------------------------------------------
HRESULT GetTextString(HWND hwnd, BSTR* pbstr)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetTextString");
WCHAR sz[MAX_PATH + 1];
WCHAR *psz = sz;
int cchText = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
// allocate memory from heap if stack buffer is insufficient
if (cchText >= MAX_PATH)
psz = new WCHAR[cchText + 1];
if (!psz)
return E_OUTOFMEMORY;
// retrieve text
HRESULT hres = S_OK;
SendMessage(hwnd, WM_GETTEXT, cchText + 1, (LPARAM)psz);
if (!*psz)
*pbstr = NULL;
else
{
*pbstr = SysAllocString(psz);
if (!*pbstr)
hres = E_OUTOFMEMORY;
}
// free memory if memory was allocated from heap
if (psz != sz)
delete [] psz;
return hres;
}
// --------------------------------------------------------------------------
//
// HRESULT GetLabelString(HWND hwnd, BSTR* pbstr)
//
// This walks backwards among peer windows to find a static field. It stops
// if it gets to the front or hits a group/tabstop, just like the dialog
// manager does.
//
// RETURN:
// HRESULT ? S_OK on success : S_FALSE or COM error on failure
// --------------------------------------------------------------------------
HRESULT GetLabelString(HWND hwnd, BSTR* pbstr)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetLabelString");
HWND hwndLabel = hwnd;
while (hwndLabel = ::GetWindow(hwndLabel, GW_HWNDPREV))
{
LONG lStyle = GetWindowLong(hwndLabel, GWL_STYLE);
// Skip if invisible
if (!(lStyle & WS_VISIBLE))
continue;
// Is this a static dude?
LRESULT lResult = SendMessage(hwndLabel, WM_GETDLGCODE, 0, 0L);
if (lResult & DLGC_STATIC)
{
// Great, we've found our label.
return GetTextString(hwndLabel, pbstr);
}
// Is this a tabstop or group? If so, bail out now.
if (lStyle & (WS_GROUP | WS_TABSTOP))
break;
}
return S_FALSE;
}
// --------------------------------------------------------------------------
//
// HRESULT StripMnemonic(BSTR bstrSrc, WCHAR** pchAmp, BOOL bStopOnAmp)
//
// This removes the mnemonic prefix. However, if we see '&&', we keep
// one '&'.
//
// --------------------------------------------------------------------------
HRESULT StripMnemonic(BSTR bstrSrc, WCHAR** pchAmp, BOOL bStopOnAmp)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "StripMnemonic");
const WCHAR amp = L'&';
if (pchAmp)
pchAmp = NULL;
WCHAR *psz = (WCHAR*)bstrSrc;
while (*psz)
{
if (*psz == amp)
{
if (*(psz + 1) == amp)
psz++;
else
{
if (pchAmp)
*pchAmp = psz;
break;
}
}
psz++;
}
// Start moving all the character up 1 position
if (!bStopOnAmp)
while (*psz)
*psz = *++psz;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// HRESULT GetWindowName(HWND hwnd, BSTR* pbstrName)
//
// --------------------------------------------------------------------------
HRESULT GetWindowName(HWND hwnd, BSTR* pbstrName)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetWindowName");
// If use a label, do that instead
if (S_OK != GetLabelString(hwnd, pbstrName) || !*pbstrName)
return S_FALSE;
// Strip out the mnemonic.
return StripMnemonic(*pbstrName, NULL, FALSE);
}
// --------------------------------------------------------------------------
//
// HRESULT GetWindowShortcut(HWND hwnd, BSTR* pbstrShortcut)
//
// --------------------------------------------------------------------------
HRESULT GetWindowShortcut(HWND hwnd, BSTR* pbstrShortcut)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "GetWindowShortcut");
if (S_OK != GetLabelString(hwnd, pbstrShortcut) || !*pbstrShortcut)
return S_FALSE;
WCHAR *pch;
StripMnemonic(*pbstrShortcut, &pch, TRUE);
// Is there a mnemonic?
if (pch)
{
// Get a localized "Alt+" string
BSTR pbstrAlt = NULL;
HRESULT hr = GetStringResource(STR_ALT, &pbstrAlt);
if (hr != S_OK || !pbstrAlt)
return hr;
// Make a string of the form "Alt+ch".
WCHAR szKey[MAX_PATH];
wcsncpy (szKey, pbstrAlt, MAX_PATH);
WCHAR *pchTemp = szKey + wcslen(szKey);
// Copy shortcut character
*pchTemp = *pch;
*(++pchTemp) = L'\0';
// Release allocated string allocate space for new string
SysFreeString(pbstrAlt);
*pbstrShortcut = SysAllocString(pchTemp);
return (*pbstrShortcut ? S_OK : E_OUTOFMEMORY);
}
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// GetWindowObject()
//
// Gets an immediate child object.
//
// --------------------------------------------------------------------------
HRESULT GetWindowObject(HWND hwndChild, VARIANT * pvar)
{
pvar->vt = VT_EMPTY;
IDispatch * pdispChild = NULL;
HRESULT hr = W32->AccessibleObjectFromWindow(hwndChild, OBJID_WINDOW, IID_IDispatch,
(void **)&pdispChild);
if (!SUCCEEDED(hr))
return(hr);
if (!pdispChild)
return(E_FAIL);
pvar->vt = VT_DISPATCH;
pvar->pdispVal = pdispChild;
return(S_OK);
}
} //namespace
//////////////////////// ListBox CListBoxSelection Methods ///////////////////////////
// --------------------------------------------------------------------------
//
// CListBoxSelection::CListBoxSelection()
//
// We AddRef() once plistFrom so that it won't go away out from us. When
// we are destroyed, we will Release() it.
//
// --------------------------------------------------------------------------
CListBoxSelection::CListBoxSelection(int iChildCur, int cSelected, LPINT lpSelection)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::CListBoxSelection");
_idChildCur = iChildCur;
_cRef = 1;
_piSel = new int[cSelected];
if (!_piSel)
_cSel = 0;
else
{
_cSel = cSelected;
memcpy(_piSel, lpSelection, cSelected*sizeof(int));
}
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::~CListBoxSelection()
//
// --------------------------------------------------------------------------
CListBoxSelection::~CListBoxSelection()
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::~CListBoxSelection");
// Free item memory
if (_piSel)
{
delete [] _piSel;
_piSel = NULL;
}
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::QueryInterface()
//
// We only respond to IUnknown and IEnumVARIANT! It is the responsibility
// of the caller to loop through the items using IEnumVARIANT interfaces,
// and get the child IDs to then pass to the parent object (or call
// directly if VT_DISPATCH--not in this case they aren't though).
//
// --------------------------------------------------------------------------
STDMETHODIMP CListBoxSelection::QueryInterface(REFIID riid, void** ppunk)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::QueryInterface");
*ppunk = NULL;
if ((riid == IID_IUnknown) || (riid == IID_IEnumVARIANT))
{
*ppunk = this;
}
else
return(E_NOINTERFACE);
((LPUNKNOWN) *ppunk)->AddRef();
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::AddRef()
//
// --------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CListBoxSelection::AddRef(void)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::AddRef");
return(++_cRef);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::Release()
//
// --------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CListBoxSelection::Release(void)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::Release");
if ((--_cRef) == 0)
{
delete this;
return 0;
}
return(_cRef);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::Next()
//
// This returns a VT_I4 which is the child ID for the parent listbox that
// returned this object for the selection collection. The caller turns
// around and passes this variant to the listbox object to get acc info
// about it.
//
// --------------------------------------------------------------------------
STDMETHODIMP CListBoxSelection::Next(ULONG celt, VARIANT* rgvar, ULONG *pceltFetched)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::Next");
// Can be NULL
if (pceltFetched)
*pceltFetched = 0;
// reset temporary variable to beginning
VARIANT *pvar = rgvar;
long cFetched = 0;
long iCur = _idChildCur;
// Loop through our items
while ((cFetched < (long)celt) && (iCur < _cSel))
{
VariantInit(pvar);
pvar->vt = VT_I4;
pvar->lVal = _piSel[iCur] + 1;
cFetched++;
iCur++;
pvar++;
}
// Initialize the variant after the last valid one just
// in case the client is looping based on invalid variants
if ((ULONG)cFetched < celt)
VariantInit(pvar);
// Advance the current position
_idChildCur = iCur;
// 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);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::Skip()
//
// -------------------------------------------------------------------------
STDMETHODIMP CListBoxSelection::Skip(ULONG celt)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::Skip");
_idChildCur += celt;
if (_idChildCur > _cSel)
_idChildCur = _cSel;
// We return S_FALSE if at the end.
return((_idChildCur >= _cSel) ? S_FALSE : S_OK);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::Reset()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListBoxSelection::Reset(void)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::Reset");
_idChildCur = 0;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListBoxSelection::Clone()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListBoxSelection::Clone(IEnumVARIANT **ppenum)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CListBoxSelection::Clone");
InitPv(ppenum);
CListBoxSelection * plistselnew = new CListBoxSelection(_idChildCur, _cSel, _piSel);
if (!plistselnew)
return(E_OUTOFMEMORY);
return(plistselnew->QueryInterface(IID_IEnumVARIANT, (void**)ppenum));
}
//////////////////////// ListBox IAccessible Methods //////////////////////////////
/*
* CLstBxWinHost::InitTypeInfo()
*
* @mfunc
* Retrieves type library
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
HRESULT CLstBxWinHost::InitTypeInfo()
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::InitTypeInfo");
return MSAA::InitTypeInfo(&_pTypeInfo);
}
/*
* CLstBxWinHost::get_accName(VARIANT varChild, BSTR *pbstrName)
*
* @mfunc
* SELF ? label of control : item text
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CLstBxWinHost::get_accName(VARIANT varChild, BSTR *pbstrName)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accName");
InitPv(pbstrName);
// Validate parameters
if (!MSAA::ValidateChild(&varChild, GetCount()))
return(E_INVALIDARG);
if (varChild.lVal == CHILDID_SELF)
{
if (_fLstType == kCombo)
return _pcbHost->get_accName(varChild, pbstrName);
else
return(MSAA::GetWindowName(_hwnd, pbstrName));
}
else
{
// Get the item text.
LRESULT lres = RichListBoxWndProc(_hwnd, LB_GETTEXTLEN, varChild.lVal-1, 0);
// First Check for error
if (lres == LB_ERR)
return S_FALSE;
if (lres > 0)
{
// allocate some buffer
*pbstrName = SysAllocStringLen(NULL, lres + 1);
if (!*pbstrName)
return E_OUTOFMEMORY;
RichListBoxWndProc(_hwnd, LB_GETTEXT, varChild.lVal-1, (LPARAM)*pbstrName);
}
}
return(S_OK);
}
/*
* CLstBxWinHost::get_accRole(VARIANT varChild, VARIANT *pvarRole)
*
* @mfunc
* Retrieves the object's Role property.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accRole(VARIANT varChild, VARIANT *pvarRole)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accRole");
InitPvar(pvarRole);
// Validate parameters
if (!MSAA::ValidateChild(&varChild, GetCount()))
return E_INVALIDARG;
pvarRole->vt = VT_I4;
if (varChild.lVal)
pvarRole->lVal = ROLE_SYSTEM_LISTITEM;
else
pvarRole->lVal = ROLE_SYSTEM_LIST;
return S_OK;
}
/*
* CLstBxWinHost::get_accState(VARIANT varChild, VARIANT *pvarState)
*
* @mfunc
* Retrieves the current state of the object or child item.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accState(VARIANT varChild, VARIANT *pvarState)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accState");
// Validate parameters
if (!MSAA::ValidateChild(&varChild, GetCount()))
return E_INVALIDARG;
InitPvar(pvarState);
if (varChild.lVal == CHILDID_SELF)
{
pvarState->vt = VT_I4;
pvarState->lVal = 0;
if (!IsWindowVisible(_hwnd))
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
if (!IsWindowEnabled(_hwnd))
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
if (_fFocus)
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
if (::GetForegroundWindow() == MSAA::GetAncestor(_hwnd, GA_ROOT))
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
return S_OK;
}
--varChild.lVal;
pvarState->vt = VT_I4;
pvarState->lVal = 0;
// Is this item selected?
if (IsSelected(varChild.lVal))
pvarState->lVal |= STATE_SYSTEM_SELECTED;
// Does it have the focus? Remember that we decremented the lVal so it
// is zero-based like listbox indeces.
if (_fFocus)
{
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
if (varChild.lVal == GetCursor())
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
}
// Is the listbox read-only?
long lStyle = GetWindowLong(_hwnd, GWL_STYLE);
if (lStyle & LBS_NOSEL)
pvarState->lVal |= STATE_SYSTEM_READONLY;
else
{
pvarState->lVal |= STATE_SYSTEM_SELECTABLE;
// Is the listbox multiple and/or extended sel? NOTE: We have
// no way to implement accSelect() EXTENDSELECTION so don't.
if (lStyle & LBS_MULTIPLESEL)
pvarState->lVal |= STATE_SYSTEM_MULTISELECTABLE;
}
// Is the item in view?
//
// SMD 09/16/97 Offscreen things are things never on the screen,
// and that doesn't apply to this. Changed from OFFSCREEN to
// INVISIBLE.
RECT rcItem;
if (!RichListBoxWndProc(_hwnd, LB_GETITEMRECT, varChild.lVal, (LPARAM)&rcItem))
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
return S_OK;
}
/*
* CLstBxWinHost::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszShortcut)
*
* @mfunc
* Retrieves an object's KeyboardShortcut property.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszShortcut)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accKeyboardShortcut");
// Validate
if (!MSAA::ValidateChild(&varChild, GetCount()))
return(E_INVALIDARG);
if ((varChild.lVal == 0) && _fLstType != kCombo)
{
InitPv(pszShortcut);
return(MSAA::GetWindowShortcut(_hwnd, pszShortcut));
}
return(DISP_E_MEMBERNOTFOUND);
}
/*
* CLstBxWinHost::get_accFocus(VARIANT *pvarChild)
*
* @mfunc
* Retrieves the child object that currently has the keyboard focus.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accFocus(VARIANT *pvarChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accFocus");
InitPvar(pvarChild);
// Are we the focus?
if (_fFocus)
{
pvarChild->vt = VT_I4;
if (GetCursor() >= 0)
pvarChild->lVal = GetCursor() + 1;
else
pvarChild->lVal = 0;
return S_OK;
}
else
return S_FALSE;
}
/*
* CLstBxWinHost::get_accSelection(VARIANT *pvarSelection)
*
* @mfunc
* Retrieves the selected children of this object.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accSelection(VARIANT *pvarSelection)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accSelection");
InitPvar(pvarSelection);
int cSel = RichListBoxWndProc(_hwnd, LB_GETSELCOUNT, 0, 0);
if (cSel <= 1)
{
// cSelected is -1, 0, or 1.
// -1 means this is a single sel listbox.
// 0 or 1 means this is multisel
if (GetCursor() < 0)
return S_FALSE;
pvarSelection->vt = VT_I4;
pvarSelection->lVal = GetCursor() + 1;
return(S_OK);
}
// Allocate memory for the list of item IDs
int * plbs = new int[cSel];
if (!plbs)
return(E_OUTOFMEMORY);
// Multiple items; must make a collection
// Get the list of selected item IDs
int j = 0;
for (long i = 0; i < GetCount(); i++)
{
if (IsSelected(i) == TRUE)
plbs[j++] = i;
}
CListBoxSelection *plbsel = new CListBoxSelection(0, cSel, plbs);
delete [] plbs;
// check if memory allocation failed
if (!plbsel)
return(E_OUTOFMEMORY);
pvarSelection->vt = VT_UNKNOWN;
return plbsel->QueryInterface(IID_IUnknown, (void**)&(pvarSelection->punkVal));
}
/*
* CLstBxWinHost::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction)
*
* @mfunc
* Retrieves a string containing a localized sentence that describes the object's default action.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::get_accDefaultAction(VARIANT varChild, BSTR *pszDefAction)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accDefaultAction");
InitPv(pszDefAction);
// Validate.
if (!MSAA::ValidateChild(&varChild, GetCount()))
return(E_INVALIDARG);
if (varChild.lVal)
return (MSAA::GetStringResource(STR_DOUBLE_CLICK, pszDefAction));
return(DISP_E_MEMBERNOTFOUND);
}
/*
* CLstBxWinHost::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild)
*
* @mfunc
* Retrieves the object's current screen location (if the object was placed on
* the screen) and optionally, the child element.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::accLocation");
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
// Validate params
if (!MSAA::ValidateChild(&varChild, GetCount()))
return E_INVALIDARG;
RECT rc;
if (!varChild.lVal)
GetClientRect(_hwnd, &rc);
else if (!RichListBoxWndProc(_hwnd, LB_GETITEMRECT, varChild.lVal-1, (LPARAM)&rc))
return S_OK;
// Convert coordinates to screen coordinates
*pcxWidth = rc.right - rc.left;
*pcyHeight = rc.bottom - rc.top;
ClientToScreen(_hwnd, (LPPOINT)&rc);
*pxLeft = rc.left;
*pyTop = rc.top;
return S_OK;
}
/*
* CLstBxWinHost::accHitTest(long xLeft, long yTop, VARIANT *pvarHit)
*
* @mfunc
* Retrieves the child object at a given point on the screen.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::accHitTest(long xLeft, long yTop, VARIANT *pvarHit)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::accHitTest");
InitPvar(pvarHit);
// Is the point in our client area?
POINT pt = {xLeft, yTop};
ScreenToClient(_hwnd, &pt);
RECT rc;
GetClientRect(_hwnd, &rc);
if (!PtInRect(&rc, pt))
return(S_FALSE);
// What item is here?
long l = GetItemFromPoint(&pt);
pvarHit->vt = VT_I4;
pvarHit->lVal = (l >= 0) ? l + 1 : 0;
return(S_OK);
}
/*
* CLstBxWinHost::accDoDefaultAction(VARIANT varChild)
*
* @mfunc
* Performs the object's default action.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::accDoDefaultAction(VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::accDoDefaultAction");
// Validate
if (!MSAA::ValidateChild(&varChild, GetCount()))
return(E_INVALIDARG);
if (varChild.lVal)
{
// this will check if WindowFromPoint at the click point is the same
// as m_hwnd, and if not, it won't click. Cool!
RECT rcLoc;
HRESULT hr = accLocation(&rcLoc.left, &rcLoc.top, &rcLoc.right, &rcLoc.bottom, varChild);
if (!SUCCEEDED (hr))
return (hr);
// Find Center of rect
POINT ptClick;
ptClick.x = rcLoc.left + (rcLoc.right/2);
ptClick.y = rcLoc.top + (rcLoc.bottom/2);
// check if hwnd at point is same as hwnd to check
if (WindowFromPoint(ptClick) != _hwnd)
return DISP_E_MEMBERNOTFOUND;
W32->BlockInput(TRUE);
// Get current cursor pos.
POINT ptCursor;
DWORD dwMouseDown, dwMouseUp;
GetCursorPos(&ptCursor);
if (GetSystemMetrics(SM_SWAPBUTTON))
{
dwMouseDown = MOUSEEVENTF_RIGHTDOWN;
dwMouseUp = MOUSEEVENTF_RIGHTUP;
}
else
{
dwMouseDown = MOUSEEVENTF_LEFTDOWN;
dwMouseUp = MOUSEEVENTF_LEFTUP;
}
// Get delta to move to center of rectangle from current
// cursor location.
ptCursor.x = ptClick.x - ptCursor.x;
ptCursor.y = ptClick.y - ptCursor.y;
// NOTE: For relative moves, USER actually multiplies the
// coords by any acceleration. But accounting for it is too
// hard and wrap around stuff is weird. So, temporarily turn
// acceleration off; then turn it back on after playback.
// Save mouse acceleration info
MOUSEINFO miSave, miNew;
if (!SystemParametersInfo(SPI_GETMOUSE, 0, &miSave, 0))
{
W32->BlockInput(FALSE);
return (DISP_E_MEMBERNOTFOUND);
}
if (miSave.MouseSpeed)
{
miNew.MouseThresh1 = 0;
miNew.MouseThresh2 = 0;
miNew.MouseSpeed = 0;
if (!SystemParametersInfo(SPI_SETMOUSE, 0, &miNew, 0))
{
W32->BlockInput(FALSE);
return (DISP_E_MEMBERNOTFOUND);
}
}
// Get # of buttons
int nButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
// mouse move to center of start button
INPUT rgInput[6];
rgInput[0].type = INPUT_MOUSE;
rgInput[0].mi.dwFlags = MOUSEEVENTF_MOVE;
rgInput[0].mi.dwExtraInfo = 0;
rgInput[0].mi.dx = ptCursor.x;
rgInput[0].mi.dy = ptCursor.y;
rgInput[0].mi.mouseData = nButtons;
int i = 1;
// MSAA's order of double click is
// WM_LBUTTONDOWN
// WM_LBUTTONUP
// WM_LBUTTONDOWN
// WM_LBUTTONUP
while (i <= 4)
{
if (i % 2)
rgInput[i].mi.dwFlags = dwMouseDown;
else
rgInput[i].mi.dwFlags = dwMouseUp;
rgInput[i].type = INPUT_MOUSE;
rgInput[i].mi.dwExtraInfo = 0;
rgInput[i].mi.dx = 0;
rgInput[i].mi.dy = 0;
rgInput[i].mi.mouseData = nButtons;
i++;
}
// move mouse back to starting location
rgInput[i].type = INPUT_MOUSE;
rgInput[i].mi.dwFlags = MOUSEEVENTF_MOVE;
rgInput[i].mi.dwExtraInfo = 0;
rgInput[i].mi.dx = -ptCursor.x;
rgInput[i].mi.dy = -ptCursor.y;
rgInput[i].mi.mouseData = nButtons;
i++;
if (!W32->SendInput(i, rgInput, sizeof(INPUT)))
MessageBeep(0);
// Restore Mouse Acceleration
if (miSave.MouseSpeed)
SystemParametersInfo(SPI_SETMOUSE, 0, &miSave, 0);
W32->BlockInput (FALSE);
return (S_OK);
}
return(DISP_E_MEMBERNOTFOUND);
}
/*
* CLstBxWinHost::accSelect(long selFlags, VARIANT varChild)
*
* @mfunc
* Modifies the selection or moves the keyboard focus according to the specified flags.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::accSelect(long selFlags, VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::accSelect");
// Validate parameters
if (!MSAA::ValidateChild(&varChild, GetCount()) || !MSAA::ValidateSelFlags(selFlags))
return(E_INVALIDARG);
if (!varChild.lVal)
return(S_FALSE);
varChild.lVal--;
long lStyle = GetWindowLong(_hwnd, GWL_STYLE);
if (lStyle & LBS_NOSEL)
return DISP_E_MEMBERNOTFOUND;
if (!IsSingleSelection())
{
// get the focused item here in case we change it.
int nFocusedItem = GetCursor();
if (selFlags & SELFLAG_TAKEFOCUS)
{
if (!_fFocus)
return(S_FALSE);
RichListBoxWndProc (_hwnd, LB_SETCARETINDEX, varChild.lVal, 0);
}
// reset and select requested item
if (selFlags & SELFLAG_TAKESELECTION)
{
// deselect the whole range of items
RichListBoxWndProc(_hwnd, LB_SETSEL, FALSE, -1);
// Select this one
RichListBoxWndProc(_hwnd, LB_SETSEL, TRUE, varChild.lVal);
}
if (selFlags & SELFLAG_EXTENDSELECTION)
{
if ((selFlags & SELFLAG_ADDSELECTION) || (selFlags & SELFLAG_REMOVESELECTION))
RichListBoxWndProc (_hwnd, LB_SELITEMRANGE, (selFlags & SELFLAG_ADDSELECTION),
MAKELPARAM(nFocusedItem, varChild.lVal));
else
{
BOOL bSelected = RichListBoxWndProc (_hwnd, LB_GETSEL, nFocusedItem, 0);
RichListBoxWndProc (_hwnd, LB_SELITEMRANGE, bSelected, MAKELPARAM(nFocusedItem,varChild.lVal));
}
}
else // not extending, check add/remove
{
if ((selFlags & SELFLAG_ADDSELECTION) || (selFlags & SELFLAG_REMOVESELECTION))
RichListBoxWndProc(_hwnd, LB_SETSEL, (selFlags & SELFLAG_ADDSELECTION), varChild.lVal);
}
// set focus to where it was before if SELFLAG_TAKEFOCUS not set
if ((selFlags & SELFLAG_TAKEFOCUS) == 0)
RichListBoxWndProc (_hwnd, LB_SETCARETINDEX, nFocusedItem, 0);
}
else // listbox is single select
{
if (selFlags & (SELFLAG_ADDSELECTION | SELFLAG_REMOVESELECTION | SELFLAG_EXTENDSELECTION))
return (E_INVALIDARG);
// single select listboxes do not allow you to set the
// focus independently of the selection, so we send a
// LB_SETCURSEL for both TAKESELECTION and TAKEFOCUS
if ((selFlags & SELFLAG_TAKESELECTION) || (selFlags & SELFLAG_TAKEFOCUS))
RichListBoxWndProc(_hwnd, LB_SETCURSEL, varChild.lVal, 0);
} // end if listbox is single select
return(S_OK);
}
/*
* CLstBxWinHost::accNavigate(long dwNavDir, VARIANT varStart, VARIANT *pvarEnd)
*
* @mfunc
* Retrieves the next or previous sibling or child object in a specified direction.
*
* @rdesc
* Returns S_OK if successful or one of the following values or a standard COM
* error code otherwise.
*/
STDMETHODIMP CLstBxWinHost::accNavigate(long dwNavDir, VARIANT varStart, VARIANT *pvarEnd)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::accNavigate");
InitPvar(pvarEnd);
// Validate parameters
if (!MSAA::ValidateChild(&varStart, GetCount()))
return(E_INVALIDARG);
// Is this something for the client (or combobox) to handle?
long lEnd = 0;
if (dwNavDir == NAVDIR_FIRSTCHILD)
{
lEnd = GetCount() ? 1 : 0;
}
else if (dwNavDir == NAVDIR_LASTCHILD)
lEnd = GetCount();
else if (varStart.lVal == CHILDID_SELF)
{
// NOTE:
// MSAA tries to make a distinction for controls by implementing 2 different types of
// interfaces for controls.
// OBJID_WINDOW - will include the windows border along with the client. This control
// should be perceived from a dialog or some window containers perspective.
// Where the control is just an abstract entity contained in the window container
// OBJID_CLIENT - only includes the client area. This interface is only concerned with
// the control itself and disregards the outside world
IAccessible* poleacc = NULL;
HRESULT hr = W32->AccessibleObjectFromWindow(_hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&poleacc);
if (!SUCCEEDED(hr))
return(hr);
// Ask it to navigate
VARIANT varStart;
VariantInit(&varStart);
varStart.vt = VT_I4;
varStart.lVal = OBJID_CLIENT;
hr = poleacc->accNavigate(dwNavDir, varStart, pvarEnd);
// Release our parent
poleacc->Release();
return(hr);
}
else
{
//long lT = varStart.lVal - 1;
switch (dwNavDir)
{
// We're a single column list box only so ignore
// these flags
//case NAVDIR_RIGHT:
//case NAVDIR_LEFT:
// break;
case NAVDIR_PREVIOUS:
case NAVDIR_UP:
// Are we in the top-most row?
lEnd = varStart.lVal - 1;
break;
case NAVDIR_NEXT:
case NAVDIR_DOWN:
lEnd = varStart.lVal + 1;
if (lEnd > GetCount())
lEnd = 0;
break;
}
}
if (lEnd)
{
pvarEnd->vt = VT_I4;
pvarEnd->lVal = lEnd;
}
return(lEnd ? S_OK : S_FALSE);
}
/*
* CLstBxWinHost::get_accParent(IDispatch **ppdispParent)
*
* @mfunc
* Retrieves the IDispatch interface of the current object's parent.
* Return S_FALSE and set the variable at ppdispParent to NULL.
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CLstBxWinHost::get_accParent(IDispatch **ppdispParent)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accParent");
AssertSz(ppdispParent != NULL, "null pointer");
if (ppdispParent == NULL)
return S_FALSE;
InitPv(ppdispParent);
HWND hwnd;
if (_fLstType != kCombo)
{
hwnd = MSAA::GetAncestor(_hwnd, GA_PARENT);
AssertSz(hwnd, "Invalid Hwnd");
if (!hwnd)
return S_FALSE;
}
else
{
if (_pcbHost)
{
hwnd = _pcbHost->_hwnd;
Assert(hwnd);
}
else
return S_FALSE;
}
HRESULT hr = W32->AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, IID_IDispatch,
(void **)ppdispParent);
#ifdef DEBUG
if (FAILED(hr))
Assert(FALSE);
#endif
return hr;
}
/*
* CLstBxWinHost::get_accChildCount(long *pcCount)
*
* @mfunc
* Retrieves the number of children belonging to the current object.
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CLstBxWinHost::get_accChildCount(long *pcCount)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::get_accChildCount");
*pcCount = GetCount();
return(S_OK);
}
//////////////////////// Combobox IAccessible Methods //////////////////////////////
// COMBOBOXES
#define INDEX_COMBOBOX 0
#define INDEX_COMBOBOX_ITEM 1
#define INDEX_COMBOBOX_BUTTON 2
#define INDEX_COMBOBOX_LIST 3
#define CCHILDREN_COMBOBOX 3
/*
* CCmbBxWinHost::InitTypeInfo()
*
* @mfunc
* Retrieves type library
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
HRESULT CCmbBxWinHost::InitTypeInfo()
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::InitTypeInfo");
return MSAA::InitTypeInfo(&_pTypeInfo);
}
/*
* CCmbBxWinHost::get_accName(VARIANT varChild, BSTR *pszName)
*
* @mfunc
* Retrieves the Name property for this object.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accName(VARIANT varChild, BSTR *pszName)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accName");
// Validate
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
// The name of the combobox, the edit inside of it, and the dropdown
// are all the same. The name of the button is Drop down/Pop up
InitPv(pszName);
if (varChild.lVal != INDEX_COMBOBOX_BUTTON)
return(MSAA::GetWindowName(_hwnd, pszName));
else
{
if (IsWindowVisible(_hwndList))
return (MSAA::GetStringResource(STR_DROPDOWN_HIDE, pszName));
else
return(MSAA::GetStringResource(STR_DROPDOWN_SHOW, pszName));
}
}
/*
* CCmbBxWinHost::get_accValue(VARIANT varChild, BSTR *pszValue)
*
* @mfunc
* Retrieves the object's Value property.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accValue(VARIANT varChild, BSTR *pszValue)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accValue");
// Validate
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
switch (varChild.lVal)
{
case INDEX_COMBOBOX:
case INDEX_COMBOBOX_ITEM:
InitPv(pszValue);
LRESULT lres;
_pserv->TxSendMessage(WM_GETTEXTLENGTH, 0, 0, &lres);
// If windows text length is 0 then MSAA searches
// for the label associated with the control
if (lres <= 0)
return MSAA::GetLabelString(_hwnd, pszValue);
GETTEXTEX gt;
memset(&gt, 0, sizeof(GETTEXTEX));
gt.cb = (lres + 1) * sizeof(WCHAR);
gt.codepage = 1200;
gt.flags = GT_DEFAULT;
*pszValue = SysAllocStringLen(NULL, lres + 1);
if (!*pszValue)
return E_OUTOFMEMORY;
_pserv->TxSendMessage(EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)*pszValue, &lres);
return S_OK;
}
return DISP_E_MEMBERNOTFOUND;
}
/*
* CCmbBxWinHost::get_accRole(VARIANT varChild, VARIANT *pvarRole)
*
* @mfunc
* Retrieves the object's Role property.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accRole(VARIANT varChild, VARIANT *pvarRole)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accRole");
// Validate--this does NOT accept a child ID.
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
pvarRole->vt = VT_I4;
switch (varChild.lVal)
{
case INDEX_COMBOBOX:
pvarRole->lVal = ROLE_SYSTEM_COMBOBOX;
break;
case INDEX_COMBOBOX_ITEM:
if (_cbType == kDropDown)
pvarRole->lVal = ROLE_SYSTEM_TEXT;
else
pvarRole->lVal = ROLE_SYSTEM_STATICTEXT;
break;
case INDEX_COMBOBOX_BUTTON:
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
break;
case INDEX_COMBOBOX_LIST:
pvarRole->lVal = ROLE_SYSTEM_LIST;
break;
default:
AssertSz(FALSE, "Invalid ChildID for child of combo box" );
}
return(S_OK);
}
/*
* CCmbBxWinHost::get_accState(VARIANT varChild, VARIANT *pvarState)
*
* @mfunc
* Retrieves the current state of the object or child item.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accState(VARIANT varChild, VARIANT *pvarState)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accState");
// Validate--this does NOT accept a child ID.
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
VARIANT var;
HRESULT hr;
IAccessible* poleacc;
InitPvar(pvarState);
pvarState->vt = VT_I4;
pvarState->lVal = 0;
HWND hwndActive = GetForegroundWindow();
switch (varChild.lVal)
{
case INDEX_COMBOBOX_BUTTON:
if (_fMousedown)
pvarState->lVal |= STATE_SYSTEM_PRESSED;
break;
case INDEX_COMBOBOX_ITEM:
if (_cbType == kDropDownList)
{
if (hwndActive == MSAA::GetAncestor(_hwnd, GA_ROOT))
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
if (_fFocus)
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
break;
}
// FALL THROUGH CASE
case INDEX_COMBOBOX:
if (!(_dwStyle & WS_VISIBLE))
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
if (_dwStyle & WS_DISABLED)
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
if (_fFocus)
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
if (hwndActive == MSAA::GetAncestor(_hwnd, GA_ROOT))
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
break;
case INDEX_COMBOBOX_LIST:
{
// First we incorporate the state of the window in general
//
VariantInit(&var);
if (FAILED(hr = MSAA::GetWindowObject(_hwndList, &var)))
return(hr);
Assert(var.vt == VT_DISPATCH);
// Get the child acc object
poleacc = NULL;
hr = var.pdispVal->QueryInterface(IID_IAccessible,
(void**)&poleacc);
var.pdispVal->Release();
if (FAILED(hr))
{
Assert(FALSE);
return(hr);
}
// Ask the child its state
VariantInit(&var);
hr = poleacc->get_accState(var, pvarState);
poleacc->Release();
if (FAILED(hr))
{
Assert(FALSE);
return(hr);
}
// The listbox is always going to be floating
//
pvarState->lVal |= STATE_SYSTEM_FLOATING;
if (_plbHost->_fDisabled)
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
else
pvarState->lVal &= ~STATE_SYSTEM_UNAVAILABLE;
if (_fListVisible)
pvarState->lVal &= ~STATE_SYSTEM_INVISIBLE;
else
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
break;
}
}
return(S_OK);
}
/*
* CCmbBxWinHost::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszShortcut)
*
* @mfunc
* Retrieves an object's KeyboardShortcut property.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszShortcut)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accKeyboardShortcut");
// Shortcut for combo is label's hotkey.
// Shortcut for dropdown (if button) is Alt+F4.
// CWO, 12/5/96, Alt+F4? F4, by itself brings down the combo box,
// but we add "Alt" to the string. Bad! Now use
// down arrow and add Alt to it via HrMakeShortcut()
// As documented in the UI style guide.
//
// As always, shortcuts only apply if the container has "focus". In other
// words, the hotkey for the combo does nothing if the parent dialog
// isn't active. And the hotkey for the dropdown does nothing if the
// combobox/edit isn't focused.
// Validate parameters
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
InitPv(pszShortcut);
if (varChild.lVal == INDEX_COMBOBOX)
{
return(MSAA::GetWindowShortcut(_hwnd, pszShortcut));
}
else if (varChild.lVal == INDEX_COMBOBOX_BUTTON)
{
return(MSAA::GetStringResource(STR_COMBOBOX_LIST_SHORTCUT, pszShortcut));
}
return DISP_E_MEMBERNOTFOUND;
}
/*
* CCmbBxWinHost::get_accFocus(VARIANT *pvarFocus)
*
* @mfunc
* Retrieves the child object that currently has the keyboard focus.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accFocus(VARIANT *pvarFocus)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accFocus");
InitPvar(pvarFocus);
// Is the current focus a child of us?
if (_fFocus)
{
pvarFocus->vt = VT_I4;
pvarFocus->lVal = 0;
}
else
{
// NOTE:
// We differ here in we don't get the foreground thread's focus window. Instead,
// we just get the current threads focus window
HWND hwnd = GetFocus();
if (IsChild(_hwnd, hwnd))
return(MSAA::GetWindowObject(hwnd, pvarFocus));
}
return(S_OK);
}
/*
* CCmbBxWinHost::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction)
*
* @mfunc
* Retrieves a string containing a localized sentence that describes the object's
* default action.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accDefaultAction");
// Validate parameters
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
if ((varChild.lVal != INDEX_COMBOBOX_BUTTON)/* || _fHasButton*/)
return DISP_E_MEMBERNOTFOUND;
// Default action of button is to press it. If pressed already, pressing
// it will pop dropdown back up. If not pressed, pressing it will pop
// dropdown down.
InitPv(pszDefaultAction);
if (IsWindowVisible(_hwndList))
return(MSAA::GetStringResource(STR_DROPDOWN_HIDE, pszDefaultAction));
else
return(MSAA::GetStringResource(STR_DROPDOWN_SHOW, pszDefaultAction));
}
/*
* CCmbBxWinHost::accSelect(long flagsSel, VARIANT varChild)
* @mfunc
* Modifies the selection or moves the keyboard focus according to the specified
* flags.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::accSelect(long flagsSel, VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::accSelect");
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX) || !MSAA::ValidateSelFlags(flagsSel))
return(E_INVALIDARG);
return(S_FALSE);
}
/*
* CCmbBxWinHost::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild)
* @mfunc
* Retrieves the object's current screen location (if the object was placed on
* the screen) and optionally, the child element.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::accLocation");
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
// Validate
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
RECT rc;
HWND hwnd = _hwnd;
switch (varChild.lVal)
{
case INDEX_COMBOBOX_BUTTON:
//if (!m_fHasButton)
// return(S_FALSE);
rc = _rcButton;
*pcxWidth = rc.right - rc.left;
*pcyHeight = rc.bottom - rc.top;
ClientToScreen(_hwnd, (LPPOINT)&rc);
break;
case INDEX_COMBOBOX_ITEM:
// Need to verify this is the currently selected item.
// if no item is selected then pass the rect of the first item in the list
_plbHost->LbGetItemRect((_plbHost->GetCursor() < 0) ? 0 : _plbHost->GetCursor(), &rc);
*pcxWidth = rc.right - rc.left;
*pcyHeight = rc.bottom - rc.top;
ClientToScreen(_hwndList, (LPPOINT)&rc);
break;
case INDEX_COMBOBOX_LIST:
hwnd = _hwndList;
// fall through!!!
case 0: //default window
GetWindowRect(hwnd, &rc);
// copy over dimensions
*pcxWidth = rc.right - rc.left;
*pcyHeight = rc.bottom - rc.top;
break;
default:
AssertSz(FALSE, "Invalid ChildID for child of combo box" );
return (S_OK);
}
*pxLeft = rc.left;
*pyTop = rc.top;
return(S_OK);
}
/*
* CCmbBxWinHost::accNavigate(long dwNav, VARIANT varStart, VARIANT* pvarEnd)
*
* @mfunc
* Retrieves the next or previous sibling or child object in a specified
* direction.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::accNavigate(long dwNav, VARIANT varStart, VARIANT* pvarEnd)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::accNavigate");
InitPvar(pvarEnd);
// Validate parameters
if (!MSAA::ValidateChild(&varStart, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
long lEnd = 0;
if (dwNav == NAVDIR_FIRSTCHILD)
{
lEnd = INDEX_COMBOBOX_ITEM;
goto GetTheChild;
}
else if (dwNav == NAVDIR_LASTCHILD)
{
dwNav = NAVDIR_PREVIOUS;
varStart.lVal = CCHILDREN_COMBOBOX + 1;
}
else if (!varStart.lVal)
{
// NOTE:
// MSAA tries to make a distinction for controls by implementing 2 different types of
// interfaces for controls.
// OBJID_WINDOW - will include the windows border along with the client. This control
// should be perceived from a dialog or some window containers perspective.
// Where the control is just an abstract entity contained in the window container
// OBJID_CLIENT - only includes the client area. This interface is only concerned with
// the control itself and disregards the outside world
IAccessible* poleacc = NULL;
HRESULT hr = W32->AccessibleObjectFromWindow(_hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&poleacc);
if (!SUCCEEDED(hr))
return(hr);
// Ask it to navigate
VARIANT varStart;
VariantInit(&varStart);
varStart.vt = VT_I4;
varStart.lVal = OBJID_CLIENT;
hr = poleacc->accNavigate(dwNav, varStart, pvarEnd);
// Release our parent
poleacc->Release();
return(hr);
}
// Map HWNDID to normal ID. We work with both (it is easier).
if (IsHWNDID(varStart.lVal))
{
HWND hWndTemp = HwndFromHWNDID(varStart.lVal);
if (hWndTemp == _hwnd)
varStart.lVal = INDEX_COMBOBOX_ITEM;
else if (hWndTemp == _hwndList)
varStart.lVal = INDEX_COMBOBOX_LIST;
else
// Don't know what the heck this is
return(S_FALSE);
}
switch (dwNav)
{
case NAVDIR_UP:
if (varStart.lVal == INDEX_COMBOBOX_LIST)
lEnd = INDEX_COMBOBOX_ITEM;
break;
case NAVDIR_DOWN:
if ((varStart.lVal != INDEX_COMBOBOX_LIST) && _fListVisible)
lEnd = INDEX_COMBOBOX_LIST;
break;
case NAVDIR_LEFT:
if (varStart.lVal == INDEX_COMBOBOX_BUTTON)
lEnd = INDEX_COMBOBOX_ITEM;
break;
case NAVDIR_RIGHT:
if ((varStart.lVal == INDEX_COMBOBOX_ITEM)/* && !(cbi.stateButton & STATE_SYSTEM_INVISIBLE)*/)
lEnd = INDEX_COMBOBOX_BUTTON;
break;
case NAVDIR_PREVIOUS:
lEnd = varStart.lVal - 1;
if ((lEnd == INDEX_COMBOBOX_LIST) && !_fListVisible)
--lEnd;
break;
case NAVDIR_NEXT:
lEnd = varStart.lVal + 1;
if (lEnd > CCHILDREN_COMBOBOX || ((lEnd == INDEX_COMBOBOX_LIST) && !_fListVisible))
lEnd = 0;
break;
}
GetTheChild:
if (lEnd)
{
// NOTE:
// MSAA tries to make a distinction for controls by implementing 2 different types of
// interfaces for controls.
// OBJID_WINDOW - will include the windows border along with the client. This control
// should be perceived from a dialog or some window containers perspective.
// Where the control is just an abstract entity contained in the window container
// OBJID_CLIENT - only includes the client area. This interface is only concerned with
// the control itself and disregards the outside world
if ((lEnd == INDEX_COMBOBOX_ITEM)/* && cbi.hwndItem*/)
return(MSAA::GetWindowObject(_hwnd, pvarEnd));
else if ((lEnd == INDEX_COMBOBOX_LIST)/* && cbi.hwndList*/)
return(MSAA::GetWindowObject(_hwndList, pvarEnd));
pvarEnd->vt = VT_I4;
pvarEnd->lVal = lEnd;
return(S_OK);
}
return(S_FALSE);
}
/*
* CCmbBxWinHost::accHitTest(long xLeft, long yTop, VARIANT *pvarEnd)
*
* @mfunc
* Retrieves the child object at a given point on the screen.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::accHitTest(long xLeft, long yTop, VARIANT *pvarEnd)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::accHitTest");
POINT pt;
RECT rc;
InitPvar(pvarEnd);
pt.x = xLeft;
pt.y = yTop;
// Check list first, in case it is a dropdown.
GetWindowRect(_hwndList, &rc);
if (_fListVisible && PtInRect(&rc, pt))
return(MSAA::GetWindowObject(_hwndList, pvarEnd));
else
{
ScreenToClient(_hwnd, &pt);
GetClientRect(_hwnd, &rc);
if (PtInRect(&_rcButton, pt))
{
pvarEnd->vt = VT_I4;
pvarEnd->lVal = INDEX_COMBOBOX_BUTTON;
}
else
{
if (!PtInRect(&rc, pt))
return(S_FALSE);
pvarEnd->vt = VT_I4;
pvarEnd->lVal = 0;
}
}
return(S_OK);
}
/*
* CCmbBxWinHost::accDoDefaultAction(VARIANT varChild)
*
* @mfunc
* Performs the object's default action.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::accDoDefaultAction(VARIANT varChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::accDoDefaultAction");
// Validate
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
if ((varChild.lVal == INDEX_COMBOBOX_BUTTON)/* && m_fHasButton*/)
{
if (_fListVisible)
PostMessage(_hwnd, WM_KEYDOWN, VK_RETURN, 0);
else
PostMessage(_hwnd, CB_SHOWDROPDOWN, TRUE, 0);
return(S_OK);
}
return DISP_E_MEMBERNOTFOUND;
}
/*
* CCmbBxWinHost::get_accSelection(VARIANT *pvarChildren)
*
* @mfunc
* Retrieves the selected children of this object.
*
* @rdesc
* Returns S_OK if successful or E_INVALIDARG or another standard COM error code
* otherwise.
*/
STDMETHODIMP CCmbBxWinHost::get_accSelection(VARIANT *pvarChildren)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accSelection");
InitPvar(pvarChildren);
return(S_FALSE);
}
/*
* CCmbBxWinHost::get_accParent(IDispatch **ppdispParent)
*
* @mfunc
* Retrieves the IDispatch interface of the current object's parent.
* Return S_FALSE and set the variable at ppdispParent to NULL.
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CCmbBxWinHost::get_accParent(IDispatch **ppdispParent)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accParent");
InitPv(ppdispParent);
if (_hwnd)
{
HWND hwnd = MSAA::GetAncestor(_hwnd, GA_PARENT);
if (hwnd)
return W32->AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
IID_IDispatch, (void **)ppdispParent);
}
return(S_FALSE);
}
/*
* CCmbBxWinHost::get_accChildCount(long *pcountChildren)
*
* @mfunc
* Retrieves the number of children belonging to the current object.
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CCmbBxWinHost::get_accChildCount(long *pcountChildren)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accChildCount");
if (pcountChildren)
*pcountChildren = CCHILDREN_COMBOBOX;
return S_OK;
}
/*
* CCmbBxWinHost::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
*
* @mfunc
* Retrieves the number of children belonging to the current object.
*
* @rdesc
* HRESULT = S_FALSE.
*/
STDMETHODIMP CCmbBxWinHost::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
{
TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CCmbBxWinHost::get_accChild");
// Validate
if (!MSAA::ValidateChild(&varChild, CCHILDREN_COMBOBOX))
return(E_INVALIDARG);
InitPv(ppdispChild);
HWND hwndChild = NULL;
switch (varChild.lVal)
{
case INDEX_COMBOBOX:
return E_INVALIDARG;
//case INDEX_COMBOBOX_ITEM:
// hwndChild = _hwnd;
// break;
case INDEX_COMBOBOX_LIST:
hwndChild = _hwndList;
break;
}
if (!hwndChild)
return(S_FALSE);
else
return(W32->AccessibleObjectFromWindow(hwndChild, OBJID_WINDOW, IID_IDispatch, (void**)ppdispChild));
}
//////////////////////// CTxtWinHost IDispatch Methods ///////////////////////////
// --------------------------------------------------------------------------
//
// CTxtWinHost::GetTypeInfoCount()
//
// This hands off to our typelib for IAccessible(). Note that
// we only implement one type of object for now. BOGUS! What about IText?
//
// --------------------------------------------------------------------------
STDMETHODIMP CTxtWinHost::GetTypeInfoCount(UINT * pctInfo)
{
HRESULT hr = InitTypeInfo();
if (SUCCEEDED(hr))
{
InitPv(pctInfo);
*pctInfo = 1;
}
return(hr);
}
// --------------------------------------------------------------------------
//
// CTxtWinHost::GetTypeInfo()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTxtWinHost::GetTypeInfo(UINT itInfo, LCID lcid,
ITypeInfo ** ppITypeInfo)
{
HRESULT hr = InitTypeInfo();
if (SUCCEEDED(hr))
{
if (ppITypeInfo == NULL)
return(E_POINTER);
InitPv(ppITypeInfo);
if (itInfo != 0)
return(TYPE_E_ELEMENTNOTFOUND);
_pTypeInfo->AddRef();
*ppITypeInfo = _pTypeInfo;
}
return(hr);
}
// --------------------------------------------------------------------------
//
// CTxtWinHost::GetIDsOfNames()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTxtWinHost::GetIDsOfNames(REFIID riid,
OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgDispID)
{
HRESULT hr = InitTypeInfo();
if (!SUCCEEDED(hr))
return(hr);
return(_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispID));
}
// --------------------------------------------------------------------------
//
// CTxtWinHost::Invoke()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTxtWinHost::Invoke(DISPID dispID, REFIID riid,
LCID lcid, WORD wFlags, DISPPARAMS * pDispParams,
VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
HRESULT hr = InitTypeInfo();
if (!SUCCEEDED(hr))
return(hr);
return(_pTypeInfo->Invoke((IAccessible *)this, dispID, wFlags,
pDispParams, pvarResult, pExcepInfo, puArgErr));
}
#endif // NOACCESSIBILITY