/* IAccessible */ HRESULT STDMETHODCALLTYPE QWindowsAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; int control = accessible->childAt(xLeft, yTop); if (control == -1) { (*pvarID).vt = VT_EMPTY; return S_FALSE; } QAccessibleInterface *acc = 0; if (control) accessible->navigate(Child, control, &acc); if (!acc) { (*pvarID).vt = VT_I4; (*pvarID).lVal = control; return S_OK; } QWindowsAccessible* wacc = new QWindowsAccessible(acc); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarID).vt = VT_DISPATCH; (*pvarID).pdispVal = iface; return S_OK; } else { delete wacc; } (*pvarID).vt = VT_EMPTY; return S_FALSE; }
/* 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 QWindowsAccessible::accHitTest(long xLeft, long yTop, VARIANT *pvarID) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) 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 { QWindowsAccessible* wacc = new QWindowsAccessible(child); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarID).vt = VT_DISPATCH; (*pvarID).pdispVal = iface; return S_OK; } } // Did not find anything (*pvarID).vt = VT_EMPTY; return S_FALSE; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accFocus(VARIANT *pvarID) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = 0; int control = accessible->navigate(QAccessible::FocusChild, 1, &acc); if (control == -1) { (*pvarID).vt = VT_EMPTY; return S_FALSE; } if (!acc || control == 0) { (*pvarID).vt = VT_I4; (*pvarID).lVal = control ? control : CHILDID_SELF; return S_OK; } QWindowsAccessible* wacc = new QWindowsAccessible(acc); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarID).vt = VT_DISPATCH; (*pvarID).pdispVal = iface; return S_OK; } else { delete wacc; } (*pvarID).vt = VT_EMPTY; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; AccessibleElement elem(childIndex, accessible); if (elem.iface) { RelationFlag rel = elem.entry ? Child : Self; int index = elem.iface->navigate(rel, elem.entry, &acc); if (index == -1) return E_INVALIDARG; } if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); return S_OK; } *ppdispChild = 0; return S_FALSE; }
IAccessible *qt_createWindowsAccessible(QAccessibleInterface *access) { QWindowsAccessible *acc = new QWindowsAccessible(access); IAccessible *iface; acc->QueryInterface(IID_IAccessible, (void**)&iface); return iface; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; int childIndex = varChildID.lVal; QAccessibleInterface *acc = 0; if (childIndex < 0) { const int entry = childIndex; QPair<QObject*, int> ref = qAccessibleRecentSentEvents()->value(entry); if (ref.first) { acc = QAccessible::queryAccessibleInterface(ref.first); if (acc && ref.second) { if (ref.second) { QAccessibleInterface *res = acc->child(ref.second - 1); delete acc; if (!res) return E_INVALIDARG; acc = res; } } } } else { if (childIndex) { acc = accessible->child(childIndex - 1); } else { // FIXME Q_ASSERT(0); } } if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); return S_OK; } *ppdispChild = 0; return S_FALSE; }
bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult) { if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) { /* For UI Automation */ } else if ((DWORD)lParam == OBJID_CLIENT) { #if 1 // Ignoring all requests while starting up // ### Maybe QPA takes care of this??? if (QApplication::startingUp() || QApplication::closingDown()) return false; #endif typedef LRESULT (WINAPI *PtrLresultFromObject)(REFIID, WPARAM, LPUNKNOWN); static PtrLresultFromObject ptrLresultFromObject = 0; static bool oleaccChecked = false; if (!oleaccChecked) { oleaccChecked = true; #if !defined(Q_OS_WINCE) ptrLresultFromObject = (PtrLresultFromObject)QSystemLibrary::resolve(QLatin1String("oleacc"), "LresultFromObject"); #endif } if (ptrLresultFromObject) { QWindow *window = QWindowsContext::instance()->findWindow(hwnd); if (window) { QAccessibleInterface *acc = window->accessibleRoot(); if (acc) { QWindowsAccessible *winacc = new QWindowsAccessible(acc); IAccessible *iface; HRESULT hr = winacc->QueryInterface(IID_IAccessible, (void**)&iface); if (SUCCEEDED(hr)) { *lResult = ptrLresultFromObject(IID_IAccessible, wParam, iface); // ref == 2 if (*lResult) { iface->Release(); // the client will release the object again, and then it will destroy itself } return true; } } } } } return false; }
// moz: [important] HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent(IDispatch** ppdispParent) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = accessible->parent(); if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispParent); if (*ppdispParent) return S_OK; } *ppdispParent = 0; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accParent(IDispatch** ppdispParent) { #ifdef DEBUG_SHOW_ATCLIENT_COMMANDS showDebug(__FUNCTION__, accessible); #endif //DEBUG_SHOW_ATCLIENT_COMMANDS if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = 0; accessible->navigate(Ancestor, 1, &acc); if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispParent); if (*ppdispParent) return S_OK; } *ppdispParent = 0; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accChild(VARIANT varChildID, IDispatch** ppdispChild) { #ifdef DEBUG_SHOW_ATCLIENT_COMMANDS showDebug(__FUNCTION__, accessible); #endif //DEBUG_SHOW_ATCLIENT_COMMANDS if (!accessible->isValid()) return E_FAIL; if (varChildID.vt == VT_EMPTY) return E_INVALIDARG; QAccessibleInterface *acc = 0; RelationFlag rel = varChildID.lVal ? Child : Self; accessible->navigate(rel, varChildID.lVal, &acc); if (acc) { QWindowsAccessible* wacc = new QWindowsAccessible(acc); wacc->QueryInterface(IID_IDispatch, (void**)ppdispChild); return S_OK; } *ppdispChild = 0; return S_FALSE; }
// moz: [important, but no need to implement up/down/left/right] HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) { showDebug(__FUNCTION__, accessible); if (!accessible->isValid()) 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) { int index = parent->indexOfChild(accessible); index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index >= 0 && index < parent->childCount()) acc = parent->child(index); delete parent; } } 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: if (QAccessibleInterface *pIface = accessible->parent()) { 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 ((accessible->relationTo(sibling) & QAccessible::Self) || sibling->state().invisible) { //ignore ourself and invisible siblings delete sibling; 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) { delete sibling; 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) { delete sibling; 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) { delete sibling; 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) { delete sibling; 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) { delete candidate; candidate = sibling; mindist = dist; } else { delete sibling; } } delete pIface; acc = candidate; } break; default: break; } if (!acc) { (*pvarEnd).vt = VT_EMPTY; return S_FALSE; } QWindowsAccessible* wacc = new QWindowsAccessible(acc); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarEnd).vt = VT_DISPATCH; (*pvarEnd).pdispVal = iface; return S_OK; } else { delete wacc; } (*pvarEnd).vt = VT_EMPTY; return S_FALSE; }
HRESULT STDMETHODCALLTYPE QWindowsAccessible::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEnd) { #ifdef DEBUG_SHOW_ATCLIENT_COMMANDS showDebug(__FUNCTION__, accessible); #endif //DEBUG_SHOW_ATCLIENT_COMMANDS if (!accessible->isValid()) return E_FAIL; QAccessibleInterface *acc = 0; int control = -1; switch(navDir) { case NAVDIR_FIRSTCHILD: control = accessible->navigate(Child, 1, &acc); break; case NAVDIR_LASTCHILD: control = accessible->navigate(Child, accessible->childCount(), &acc); break; case NAVDIR_NEXT: case NAVDIR_PREVIOUS: if (!varStart.lVal){ QAccessibleInterface *parent = 0; accessible->navigate(Ancestor, 1, &parent); if (parent) { int index = parent->indexOfChild(accessible); index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= parent->childCount()) control = parent->navigate(Child, index, &acc); delete parent; } } else { int index = varStart.lVal; index += (navDir == NAVDIR_NEXT) ? 1 : -1; if (index > 0 && index <= accessible->childCount()) control = accessible->navigate(Child, index, &acc); } break; case NAVDIR_UP: control = accessible->navigate(Up, varStart.lVal, &acc); break; case NAVDIR_DOWN: control = accessible->navigate(Down, varStart.lVal, &acc); break; case NAVDIR_LEFT: control = accessible->navigate(Left, varStart.lVal, &acc); break; case NAVDIR_RIGHT: control = accessible->navigate(Right, varStart.lVal, &acc); break; default: break; } if (control == -1) { (*pvarEnd).vt = VT_EMPTY; return S_FALSE; } if (!acc) { (*pvarEnd).vt = VT_I4; (*pvarEnd).lVal = control; return S_OK; } QWindowsAccessible* wacc = new QWindowsAccessible(acc); IDispatch *iface = 0; wacc->QueryInterface(IID_IDispatch, (void**)&iface); if (iface) { (*pvarEnd).vt = VT_DISPATCH; (*pvarEnd).pdispVal = iface; return S_OK; } else { delete wacc; } (*pvarEnd).vt = VT_EMPTY; return S_FALSE; }