/* * IACCESS.CPP * * Purpose: * Implemenation of IAccessibility for listbox and combobox * * Original Author: * Jerry Kim * * History: * 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(>, 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)>, (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