/* IAccessible IAccessible::accHitTest documents the value returned in pvarID like this: | *Point location* | *vt member* | *Value member* | +========================================================+=============+=========================+ | Outside of the object's boundaries, and either inside | VT_EMPTY | None. | | or outside of the object's bounding rectangle. | | | +--------------------------------------------------------+-------------+-------------------------+ | Within the object but not within a child element or a | VT_I4 | lVal is CHILDID_SELF | | child object. | | | +--------------------------------------------------------+-------------+-------------------------+ | Within a child element. | VT_I4 | lVal contains | | | | the child ID. | +--------------------------------------------------------+-------------+-------------------------+ | Within a child object. | VT_DISPATCH | pdispVal is set to the | | | | child object's IDispatch| | | | interface pointer | +--------------------------------------------------------+-------------+-------------------------+ */ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessibleInterface *child = accessible->childAt(xLeft, yTop); if (child == 0) { // no child found, return this item if it contains the coordinates if (accessible->rect().contains(xLeft, yTop)) { (*pvarID).vt = VT_I4; (*pvarID).lVal = CHILDID_SELF; return S_OK; } } else { IAccessible *iface = QWindowsAccessibility::wrap(child); if (iface) { (*pvarID).vt = VT_DISPATCH; (*pvarID).pdispVal = iface; return S_OK; } } // Did not find anything (*pvarID).vt = VT_EMPTY; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDescription(VARIANT varID, BSTR* pszDescription) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QString descr; if (varID.lVal) { QAccessibleInterface *child = childPointer(accessible, varID); if (!child) return E_FAIL; descr = child->text(QAccessible::Description); } else { descr = accessible->text(QAccessible::Description); } if (descr.size()) { *pszDescription = QStringToBSTR(descr); return S_OK; } *pszDescription = 0; return S_FALSE; }
/**************************************************************\ * * * IUnknown * * * **************************************************************/ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::QueryInterface(REFIID id, LPVOID *iface) { *iface = 0; QByteArray strIID = IIDToString(id); if (!strIID.isEmpty()) { QString ss; QDebug dbg(&ss); dbg << accessibleInterface(); accessibleDebug("QWindowsIA2Accessible::QI() - IID:%s, iface:%s ", strIID.constData(), qPrintable(ss)); } if (id == IID_IUnknown) { *iface = (IUnknown*)(IDispatch*)this; } else if (id == IID_IDispatch) { *iface = (IDispatch*)this; } else if (id == IID_IAccessible) { *iface = (IAccessible*)this; } else if (id == IID_IOleWindow) { *iface = (IOleWindow*)this; } if (*iface) { AddRef(); return S_OK; } return E_NOINTERFACE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accName(VARIANT varID, BSTR* pszName) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QString name; if (varID.lVal) { QAccessibleInterface *child = childPointer(accessible, varID); if (!child) return E_FAIL; name = child->text(QAccessible::Name); if (name.isEmpty()) { if (QAccessibleInterface *labelInterface = relatedInterface(child, QAccessible::Label)) { name = labelInterface->text(QAccessible::Name); } } } else { name = accessible->text(QAccessible::Name); if (name.isEmpty()) { if (QAccessibleInterface *labelInterface = relatedInterface(accessible, QAccessible::Label)) { name = labelInterface->text(QAccessible::Name); } } } if (name.size()) { *pszName = QStringToBSTR(name); return S_OK; } *pszName = 0; return S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accRole(VARIANT varID, VARIANT *pvarRole) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessible::Role role; if (varID.lVal) { QAccessibleInterface *child = childPointer(accessible, varID); if (!child) return E_FAIL; role = child->role(); } else { role = accessible->role(); } if (role != QAccessible::NoRole) { if (role >= QAccessible::LayeredPane) { // This block should hopefully only be entered if the AT client // does not support IAccessible2, since it should prefer IA2::role() then. if (role == QAccessible::LayeredPane) role = QAccessible::Pane; else role = QAccessible::Client; } (*pvarRole).vt = VT_I4; (*pvarRole).lVal = role; } else { (*pvarRole).vt = VT_EMPTY; } return S_OK; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accSelect(long flagsSelect, VARIANT varID) { Q_UNUSED(flagsSelect); Q_UNUSED(varID); QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; bool res = false; /* ### Check for accessibleTableInterface() or accessibleTextInterface() ### and if there are no ia2 interfaces we should do nothing?? if (flagsSelect & SELFLAG_TAKEFOCUS) res = accessible()->doAction(SetFocus, varID.lVal, QVariantList()); if (flagsSelect & SELFLAG_TAKESELECTION) { accessible()->doAction(ClearSelection, 0, QVariantList()); res = accessible()->doAction(AddToSelection, varID.lVal, QVariantList()); } if (flagsSelect & SELFLAG_EXTENDSELECTION) res = accessible()->doAction(ExtendSelection, varID.lVal, QVariantList()); if (flagsSelect & SELFLAG_ADDSELECTION) res = accessible()->doAction(AddToSelection, varID.lVal, QVariantList()); if (flagsSelect & SELFLAG_REMOVESELECTION) res = accessible()->doAction(RemoveSelection, varID.lVal, QVariantList()); */ return res ? S_OK : S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accValue(VARIANT varID, BSTR* pszValue) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (varID.vt != VT_I4) return E_INVALIDARG; if (!accessible || !accessible->isValid() || varID.lVal) { return E_FAIL; } QString value; if (accessible->valueInterface()) { value = QString::number(accessible->valueInterface()->currentValue().toDouble()); } else { value = accessible->text(QAccessible::Value); } if (!value.isNull()) { *pszValue = QStringToBSTR(value); return S_OK; } *pszValue = 0; accessibleDebug("return S_FALSE"); return S_FALSE; }
/**************************************************************\ * * * IUnknown * * * **************************************************************/ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::QueryInterface(REFIID id, LPVOID *iface) { *iface = 0; QByteArray strIID = IIDToString(id); if (!strIID.isEmpty()) { qCDebug(lcQpaAccessibility) << "QWindowsIA2Accessible::QI() - IID:" << strIID << ", iface:" << accessibleInterface(); } if (id == IID_IUnknown) { *iface = (IUnknown*)(IDispatch*)this; } else if (id == IID_IDispatch) { *iface = (IDispatch*)this; } else if (id == IID_IAccessible) { *iface = (IAccessible*)this; } else if (id == IID_IOleWindow) { *iface = (IOleWindow*)this; } if (*iface) { AddRef(); return S_OK; } return E_NOINTERFACE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChildCount(long* pcountChildren) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; *pcountChildren = accessible->childCount(); return S_OK; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accDefaultAction(VARIANT varID, BSTR* pszDefaultAction) { Q_UNUSED(varID); QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; *pszDefaultAction = 0; if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) { const QString def = actionIface->actionNames().value(0); if (!def.isEmpty()) *pszDefaultAction = QStringToBSTR(def); } return *pszDefaultAction ? S_OK : S_FALSE; }
/* Properties and methods */ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accDoDefaultAction(VARIANT varID) { Q_UNUSED(varID); QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) { const QString def = actionIface->actionNames().value(0); if (!def.isEmpty()) { actionIface->doAction(def); return S_OK; } } return S_FALSE; }
/**************************************************************\ * IOleWindow * **************************************************************/ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::GetWindow(HWND *phwnd) { *phwnd = 0; QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QWindow *window = QWindowsAccessibility::windowHelper(accessible); if (!window) return E_FAIL; QPlatformNativeInterface *platform = QGuiApplication::platformNativeInterface(); Q_ASSERT(platform); *phwnd = (HWND)platform->nativeResourceForWindow("handle", window); accessibleDebug("QWindowsAccessible::GetWindow(): %p", *phwnd); return S_OK; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accParent(IDispatch** ppdispParent) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessibleInterface *acc = accessible->parent(); if (acc) { if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) { *ppdispParent = iface; return S_OK; } } *ppdispParent = 0; return S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; if (varChildID.vt != VT_I4) return E_INVALIDARG; QAccessibleInterface *acc = childPointer(accessible, varChildID); if (acc) { *ppdispChild = QWindowsAccessibility::wrap(acc); return S_OK; } return E_FAIL; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accKeyboardShortcut(VARIANT varID, BSTR *pszKeyboardShortcut) { Q_UNUSED(varID); QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; *pszKeyboardShortcut = 0; if (QAccessibleActionInterface *actionIface = accessible->actionInterface()) { const QString def = actionIface->actionNames().value(0); if (!def.isEmpty()) { const QString keyBoardShortCut = actionIface->keyBindingsForAction(def).value(0); if (!keyBoardShortCut.isEmpty()) *pszKeyboardShortcut = QStringToBSTR(keyBoardShortCut); } } return *pszKeyboardShortcut ? S_OK : S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varID) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessibleInterface *acc = childPointer(accessible, varID); if (!acc || !acc->isValid()) return E_FAIL; const QRect rect = acc->rect(); *pxLeft = rect.x(); *pyTop = rect.y(); *pcxWidth = rect.width(); *pcyHeight = rect.height(); return S_OK; }
/*! \internal Can return: +-------------+------------------------------------------------------------------------------+ | VT_EMPTY | None. Neither this object nor any of its children has the keyboard focus. | +-------------+------------------------------------------------------------------------------+ | VT_I4 | lVal is CHILDID_SELF. The object itself has the keyboard focus. | +-------------+------------------------------------------------------------------------------+ | VT_I4 | lVal contains the child ID of the child element that has the keyboard focus. | +-------------+------------------------------------------------------------------------------+ | VT_DISPATCH | pdispVal member is the address of the IDispatch interface for the child | | | object that has the keyboard focus. | +-------------+------------------------------------------------------------------------------+ moz: [important] */ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accFocus(VARIANT *pvarID) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; if (QAccessibleInterface *acc = accessible->focusChild()) { if (acc == accessible) { (*pvarID).vt = VT_I4; (*pvarID).lVal = CHILDID_SELF; return S_OK; } else { if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) { (*pvarID).vt = VT_DISPATCH; (*pvarID).pdispVal = iface; return S_OK; } } } (*pvarID).vt = VT_EMPTY; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accSelection(VARIANT *pvarChildren) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; int cc = accessible->childCount(); QVector<int> sel(cc); int selIndex = 0; for (int i = 0; i < cc; ++i) { bool isSelected = false; QAccessibleInterface *child = accessible->child(i); if (child) { isSelected = child->state().selected; } if (isSelected) sel[selIndex++] = i+1; } sel.resize(selIndex); if (sel.isEmpty()) { (*pvarChildren).vt = VT_EMPTY; return S_FALSE; } if (sel.size() == 1) { (*pvarChildren).vt = VT_I4; (*pvarChildren).lVal = sel[0]; return S_OK; } IEnumVARIANT *iface = new QWindowsEnumerate(sel); IUnknown *uiface; iface->QueryInterface(IID_IUnknown, (void**)&uiface); (*pvarChildren).vt = VT_UNKNOWN; (*pvarChildren).punkVal = uiface; return S_OK; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accHelp(VARIANT varID, BSTR *pszHelp) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QString help; if (varID.lVal) { QAccessibleInterface *child = childPointer(accessible, varID); if (!child) return E_FAIL; help = child->text(QAccessible::Help); } else { help = accessible->text(QAccessible::Help); } if (help.size()) { *pszHelp = QStringToBSTR(help); return S_OK; } *pszHelp = 0; return S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::get_accState(VARIANT varID, VARIANT *pvarState) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessible::State state; if (varID.lVal) { QAccessibleInterface *child = childPointer(accessible, varID); if (!child) return E_FAIL; state = child->state(); } else { state = accessible->state(); } LONG st = 0; if (state.animated) st |= STATE_SYSTEM_ANIMATED; if (state.busy) st |= STATE_SYSTEM_BUSY; if (state.checked) st |= STATE_SYSTEM_CHECKED; if (state.collapsed) st |= STATE_SYSTEM_COLLAPSED; if (state.defaultButton) st |= STATE_SYSTEM_DEFAULT; if (state.expanded) st |= STATE_SYSTEM_EXPANDED; if (state.extSelectable) st |= STATE_SYSTEM_EXTSELECTABLE; if (state.focusable) st |= STATE_SYSTEM_FOCUSABLE; if (state.focused) st |= STATE_SYSTEM_FOCUSED; if (state.hasPopup) st |= STATE_SYSTEM_HASPOPUP; if (state.hotTracked) st |= STATE_SYSTEM_HOTTRACKED; if (state.invisible) st |= STATE_SYSTEM_INVISIBLE; if (state.linked) st |= STATE_SYSTEM_LINKED; if (state.marqueed) st |= STATE_SYSTEM_MARQUEED; if (state.checkStateMixed) st |= STATE_SYSTEM_MIXED; if (state.movable) st |= STATE_SYSTEM_MOVEABLE; if (state.multiSelectable) st |= STATE_SYSTEM_MULTISELECTABLE; if (state.offscreen) st |= STATE_SYSTEM_OFFSCREEN; if (state.pressed) st |= STATE_SYSTEM_PRESSED; if (state.passwordEdit) st |= STATE_SYSTEM_PROTECTED; if (state.readOnly) st |= STATE_SYSTEM_READONLY; if (state.selectable) st |= STATE_SYSTEM_SELECTABLE; if (state.selected) st |= STATE_SYSTEM_SELECTED; if (state.selfVoicing) st |= STATE_SYSTEM_SELFVOICING; if (state.sizeable) st |= STATE_SYSTEM_SIZEABLE; if (state.traversed) st |= STATE_SYSTEM_TRAVERSED; (*pvarState).vt = VT_I4; (*pvarState).lVal = st; return S_OK; }
HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::put_accName(VARIANT, BSTR) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); return DISP_E_MEMBERNOTFOUND; }
// moz: [important, but no need to implement up/down/left/right] HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) { QAccessibleInterface *accessible = accessibleInterface(); accessibleDebugClientCalls(accessible); if (!accessible) return E_FAIL; QAccessibleInterface *acc = 0; switch (navDir) { case NAVDIR_FIRSTCHILD: acc = accessible->child(0); break; case NAVDIR_LASTCHILD: acc = accessible->child(accessible->childCount() - 1); break; case NAVDIR_NEXT: case NAVDIR_PREVIOUS: if (!varStart.lVal){ QAccessibleInterface *parent = accessible->parent(); if (parent && parent->isValid()) { int index = parent->indexOfChild(accessible); index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index >= 0 && index < parent->childCount()) acc = parent->child(index); } } else { int index = varStart.lVal; index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= accessible->childCount()) acc = accessible->child(index - 1); } break; // Geometrical case NAVDIR_UP: case NAVDIR_DOWN: case NAVDIR_LEFT: case NAVDIR_RIGHT: { QAccessibleInterface *pIface = accessible->parent(); if (pIface && pIface->isValid()) { const int indexOfOurself = pIface->indexOfChild(accessible); QRect startg = accessible->rect(); QPoint startc = startg.center(); QAccessibleInterface *candidate = 0; unsigned mindist = UINT_MAX; // will work on screen sizes at least up to 46340x46340 const int sibCount = pIface->childCount(); for (int i = 0; i < sibCount; ++i) { QAccessibleInterface *sibling = 0; sibling = pIface->child(i); Q_ASSERT(sibling); if (i == indexOfOurself || sibling->state().invisible) { //ignore ourself and invisible siblings continue; } QRect sibg = sibling->rect(); QPoint sibc = sibg.center(); QPoint sibp; QPoint startp; QPoint distp; switch (navDir) { case NAVDIR_LEFT: startp = QPoint(startg.left(), startg.top() + startg.height() / 2); sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2); if (QPoint(sibc - startc).x() >= 0) { continue; } distp = sibp - startp; break; case NAVDIR_RIGHT: startp = QPoint(startg.right(), startg.top() + startg.height() / 2); sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2); if (QPoint(sibc - startc).x() <= 0) { continue; } distp = sibp - startp; break; case NAVDIR_UP: startp = QPoint(startg.left() + startg.width() / 2, startg.top()); sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom()); if (QPoint(sibc - startc).y() >= 0) { continue; } distp = sibp - startp; break; case NAVDIR_DOWN: startp = QPoint(startg.left() + startg.width() / 2, startg.bottom()); sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top()); if (QPoint(sibc - startc).y() <= 0) { continue; } distp = sibp - startp; break; default: break; } // Since we're *comparing* (and not measuring) distances, we can compare the // squared distance, (thus, no need to take the sqrt()). unsigned dist = distp.x() * distp.x() + distp.y() * distp.y(); if (dist < mindist) { candidate = sibling; mindist = dist; } } acc = candidate; } } break; default: break; } if (!acc) { (*pvarEnd).vt = VT_EMPTY; return S_FALSE; } if (IAccessible *iface = QWindowsAccessibility::wrap(acc)) { (*pvarEnd).vt = VT_DISPATCH; (*pvarEnd).pdispVal = iface; return S_OK; } (*pvarEnd).vt = VT_EMPTY; return S_FALSE; }