Move all usages of Relation enum values to QAccessible::relations()
authorJan-Arve Saether <jan-arve.saether@nokia.com>
Thu, 9 Feb 2012 10:20:34 +0000 (11:20 +0100)
committerQt by Nokia <qt-info@nokia.com>
Fri, 10 Feb 2012 14:21:46 +0000 (15:21 +0100)
Next step is to remove navigate(), but that has to be done in
qtdeclarative first.

Change-Id: I01ea1386c092446be04cc19d0f70adf53f094adc
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@nokia.com>
src/gui/accessible/qaccessible.cpp
src/gui/accessible/qaccessible.h
src/plugins/accessible/widgets/simplewidgets.cpp
src/plugins/accessible/widgets/simplewidgets.h
src/plugins/platforms/windows/qwindowsaccessibility.cpp
src/widgets/accessible/qaccessiblewidget.cpp
src/widgets/accessible/qaccessiblewidget.h
tests/auto/other/qaccessibility/tst_qaccessibility.cpp

index dd0468d..6664e5d 100644 (file)
@@ -894,17 +894,19 @@ QAccessibleInterface *QAccessibleEvent::accessibleInterface() const
 */
 QAccessible::Relation QAccessibleInterface::relationTo(const QAccessibleInterface *) const
 {
-    return QAccessible::Unrelated;
+    return QAccessible::Relation();
 }
 
 /*!
     Returns the meaningful relations to other widgets. Usually this will not return parent/child
     relations, unless they are handled in a specific way such as in tree views.
     It will typically return the labelled-by and label relations.
+    It should never return itself.
 
     \sa relationTo(), navigate()
 */
-QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > QAccessibleInterface::relations() const
+QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >
+QAccessibleInterface::relations(QAccessible::Relation /*match = QAccessible::AllRelations*/) const
 {
     return QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >();
 }
index 214ec20..9fc13a8 100644 (file)
@@ -306,12 +306,11 @@ public:
     };
 
     enum RelationFlag {
-        Unrelated     = 0x00000000,
         Label         = 0x00020000,
         Labelled      = 0x00040000,
         Controller    = 0x00080000,
         Controlled    = 0x00100000,
-        LogicalMask   = 0x00ff0000
+        AllRelations  = 0xffffffff
     };
     Q_DECLARE_FLAGS(Relation, RelationFlag)
 
@@ -380,7 +379,7 @@ public:
 
     // relations
     virtual QAccessible::Relation relationTo(const QAccessibleInterface *other) const;
-    virtual QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > relations() const;
+    virtual QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > relations(QAccessible::Relation match = QAccessible::AllRelations) const;
     virtual QAccessibleInterface *focusChild() const;
 
     virtual QAccessibleInterface *childAt(int x, int y) const = 0;
index 652150f..0fdd449 100644 (file)
@@ -54,6 +54,7 @@
 #include <qlineedit.h>
 #include <qstyle.h>
 #include <qstyleoption.h>
+#include <QtCore/qvarlengtharray.h>
 
 #ifdef Q_OS_MAC
 #include <qfocusframe.h>
@@ -427,52 +428,36 @@ QString QAccessibleDisplay::text(QAccessible::Text t) const
     return qt_accStripAmp(str);
 }
 
-QAccessible::Relation QAccessibleDisplay::relationTo(const QAccessibleInterface *other) const
+/*! \reimp */
+QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >
+QAccessibleDisplay::relations(QAccessible::Relation match /*= QAccessible::AllRelations*/) const
 {
-    QAccessible::Relation relation = QAccessibleWidget::relationTo(other);
+    QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > rels = QAccessibleWidget::relations(match);
+    if (match & QAccessible::Labelled) {
+        QVarLengthArray<QObject *, 4> relatedObjects;
 
-    QObject *o = other->object();
-    QLabel *label = qobject_cast<QLabel*>(object());
-    if (label) {
 #ifndef QT_NO_SHORTCUT
-        if (o == label->buddy())
-            relation |= QAccessible::Label;
+        if (QLabel *label = qobject_cast<QLabel*>(object())) {
+            relatedObjects.append(label->buddy());
 #endif
 #ifndef QT_NO_GROUPBOX
-    } else {
-        QGroupBox *groupbox = qobject_cast<QGroupBox*>(object());
-        if (groupbox && !groupbox->title().isEmpty())
-            if (groupbox->children().contains(o))
-                relation |= QAccessible::Label;
-#endif
-    }
-    return relation;
-}
-
-int QAccessibleDisplay::navigate(QAccessible::RelationFlag rel, int entry, QAccessibleInterface **target) const
-{
-    *target = 0;
-    if (rel == QAccessible::Labelled) {
-        QObject *targetObject = 0;
-        QLabel *label = qobject_cast<QLabel*>(object());
-        if (label) {
-#ifndef QT_NO_SHORTCUT
-            if (entry == 1)
-                targetObject = label->buddy();
-#endif
-#ifndef QT_NO_GROUPBOX
-        } else {
-            QGroupBox *groupbox = qobject_cast<QGroupBox*>(object());
-            if (groupbox && !groupbox->title().isEmpty())
-                *target = child(entry - 1);
+        } else if (QGroupBox *groupbox = qobject_cast<QGroupBox*>(object())) {
+            if (!groupbox->title().isEmpty()) {
+                const QList<QWidget*> kids = childWidgets(widget());
+                for (int i = 0; i < kids.count(); ++i) {
+                    relatedObjects.append(kids.at(i));
+                }
+            }
 #endif
         }
-        if (targetObject)
-            *target = QAccessible::queryAccessibleInterface(targetObject);
-        if (*target)
-            return 0;
+        for (int i = 0; i < relatedObjects.count(); ++i) {
+            const QAccessible::Relation rel = QAccessible::Labelled;
+            QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(relatedObjects.at(i));
+            if (iface)
+                rels.append(qMakePair(iface, rel));
+        }
     }
-    return QAccessibleWidget::navigate(rel, entry, target);
+    return rels;
 }
 
 void *QAccessibleDisplay::interface_cast(QAccessible::InterfaceType t)
index bbdecec..c228775 100644 (file)
@@ -104,8 +104,7 @@ public:
     QString text(QAccessible::Text t) const;
     QAccessible::Role role() const;
 
-    QAccessible::Relation relationTo(const QAccessibleInterface *other) const;
-    int navigate(QAccessible::RelationFlag, int entry, QAccessibleInterface **target) const;
+    QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >relations(QAccessible::Relation match = QAccessible::AllRelations) const;
     void *interface_cast(QAccessible::InterfaceType t);
 
     // QAccessibleImageInterface
index 2c49d86..e36e255 100644 (file)
@@ -988,6 +988,17 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accKeyboardShortcut(VARIANT va
     return *pszKeyboardShortcut ? S_OK : S_FALSE;
 }
 
+static QAccessibleInterface *relatedInterface(QAccessibleInterface *iface, QAccessible::RelationFlag flag)
+{
+    typedef QPair<QAccessibleInterface *, QAccessible::Relation> RelationPair;
+    QVector<RelationPair> rels = iface->relations(flag);
+
+    for (int i = 1; i < rels.count(); ++i)
+        delete rels.at(i).first;
+
+    return rels.value(0).first;
+}
+
 // moz: [important]
 HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* pszName)
 {
@@ -1001,8 +1012,20 @@ HRESULT STDMETHODCALLTYPE QWindowsAccessible::get_accName(VARIANT varID, BSTR* p
         if (!child)
             return E_FAIL;
         name = child->text(QAccessible::Name);
+        if (name.isEmpty()) {
+            if (QAccessibleInterface *labelInterface = relatedInterface(child.data(), QAccessible::Label)) {
+                name = labelInterface->text(QAccessible::Name);
+                delete labelInterface;
+            }
+        }
     } else {
         name = accessible->text(QAccessible::Name);
+        if (name.isEmpty()) {
+            if (QAccessibleInterface *labelInterface = relatedInterface(accessible, QAccessible::Label)) {
+                name = labelInterface->text(QAccessible::Name);
+                delete labelInterface;
+            }
+        }
     }
     if (name.size()) {
         *pszName = QStringToBSTR(name);
index 56ba990..feac427 100644 (file)
@@ -327,37 +327,76 @@ static inline bool isAncestor(const QObject *obj, const QObject *child)
     return false;
 }
 
-
 /*! \reimp */
-QAccessible::Relation QAccessibleWidget::relationTo(const QAccessibleInterface *other) const
+QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >
+QAccessibleWidget::relations(QAccessible::Relation match /*= QAccessible::AllRelations*/) const
 {
-    QAccessible::Relation relation = QAccessible::Unrelated;
-    if (d->asking == this) // recursive call
-        return relation;
-
-    QObject *o = other ? other->object() : 0;
-    if (!o)
-        return relation;
+    QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > rels;
+    if (match & QAccessible::Label) {
+        const QAccessible::Relation rel = QAccessible::Label;
+        if (QWidget *parent = widget()->parentWidget()) {
+#ifndef QT_NO_SHORTCUT
+            // first check for all siblings that are labels to us
+            // ideally we would go through all objects and check, but that
+            // will be too expensive
+            const QList<QWidget*> kids = childWidgets(parent);
+            for (int i = 0; i < kids.count(); ++i) {
+                if (QLabel *labelSibling = qobject_cast<QLabel*>(kids.at(i))) {
+                    if (labelSibling->buddy() == widget()) {
+                        QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(labelSibling);
+                        rels.append(qMakePair(iface, rel));
+                    }
+                }
+            }
+#endif
+#ifndef QT_NO_GROUPBOX
+            QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
+            if (groupbox && !groupbox->title().isEmpty()) {
+                QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(groupbox);
+                rels.append(qMakePair(iface, rel));
+            }
+#endif
+        }
+    }
 
-    QACConnectionObject *connectionObject = (QACConnectionObject*)object();
-    for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
-        if (connectionObject->isSender(o, d->primarySignals.at(sig).toAscii())) {
-            relation |= QAccessible::Controller;
-            break;
+    if (match & QAccessible::Controller) {
+        const QAccessible::Relation rel = QAccessible::Controller;
+        QACConnectionObject *connectionObject = (QACConnectionObject*)object();
+        const QObjectList senderList = connectionObject->senderList();
+        for (int s = 0; s < senderList.count(); ++s) {
+            QObject *sender = senderList.at(s);
+            if (sender->isWidgetType() && sender != object()) {
+                QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(sender);
+                QACConnectionObject *connectionSender = (QACConnectionObject*)sender;
+                QStringList senderPrimarySignals = static_cast<QAccessibleWidget*>(iface)->d->primarySignals;
+                for (int sig = 0; sig < senderPrimarySignals.count(); ++sig) {
+                    const QByteArray strSignal = senderPrimarySignals.at(sig).toAscii();
+                    if (connectionSender->isSender(object(), strSignal.constData()))
+                        rels.append(qMakePair(iface, rel));
+                }
+            }
         }
     }
-    // test for passive relationships.
-    // d->asking protects from endless recursion.
-    d->asking = this;
-    int inverse = other->relationTo(this);
-    d->asking = 0;
 
-    if (inverse & QAccessible::Controller)
-        relation |= QAccessible::Controlled;
-    if (inverse & QAccessible::Label)
-        relation |= QAccessible::Labelled;
+    if (match & QAccessible::Controlled) {
+        QObjectList allReceivers;
+        QACConnectionObject *connectionObject = (QACConnectionObject*)object();
+        for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
+            const QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toAscii());
+            allReceivers += receivers;
+        }
+
+        allReceivers.removeAll(object());  //### The object might connect to itself internally
 
-    return relation;
+        for (int i = 0; i < allReceivers.count(); ++i) {
+            const QAccessible::Relation rel = QAccessible::Controlled;
+            QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(allReceivers.at(i));
+            if (iface)
+                rels.append(qMakePair(iface, rel));
+        }
+    }
+
+    return rels;
 }
 
 /*! \reimp */
@@ -394,96 +433,6 @@ QAccessibleInterface *QAccessibleWidget::focusChild() const
 }
 
 /*! \reimp */
-int QAccessibleWidget::navigate(QAccessible::RelationFlag relation, int entry,
-                                QAccessibleInterface **target) const
-{
-    if (!target)
-        return -1;
-
-    *target = 0;
-    QObject *targetObject = 0;
-
-    switch (relation) {
-    // Logical
-    case QAccessible::Label:
-        if (entry > 0) {
-            QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
-            if (!pIface)
-                return -1;
-
-            // first check for all siblings that are labels to us
-            // ideally we would go through all objects and check, but that
-            // will be too expensive
-            int sibCount = pIface->childCount();
-            QAccessibleInterface *candidate = 0;
-            for (int i = 0; i < sibCount && entry; ++i) {
-                candidate = pIface->child(i);
-                Q_ASSERT(candidate);
-                if (candidate->relationTo(this) & QAccessible::Label)
-                    --entry;
-                if (!entry)
-                    break;
-
-                delete candidate;
-                candidate = 0;
-            }
-            if (!candidate) {
-                if (pIface->relationTo(this) & QAccessible::Label)
-                    --entry;
-                if (!entry)
-                    candidate = pIface;
-            }
-            if (pIface != candidate)
-                delete pIface;
-
-            *target = candidate;
-            if (*target)
-                return 0;
-        }
-        break;
-    case QAccessible::Labelled: // only implemented in subclasses
-        break;
-    case QAccessible::Controller:
-        if (entry > 0) {
-            // check all senders we are connected to,
-            // and figure out which one are controllers to us
-            QACConnectionObject *connectionObject = (QACConnectionObject*)object();
-            QObjectList allSenders = connectionObject->senderList();
-            QObjectList senders;
-            for (int s = 0; s < allSenders.size(); ++s) {
-                QObject *sender = allSenders.at(s);
-                QAccessibleInterface *candidate = QAccessible::queryAccessibleInterface(sender);
-                if (!candidate)
-                    continue;
-                if (candidate->relationTo(this) & QAccessible::Controller)
-                    senders << sender;
-                delete candidate;
-            }
-            if (entry <= senders.size())
-                targetObject = senders.at(entry-1);
-        }
-        break;
-    case QAccessible::Controlled:
-        if (entry > 0) {
-            QObjectList allReceivers;
-            QACConnectionObject *connectionObject = (QACConnectionObject*)object();
-            for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
-                QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toAscii());
-                allReceivers += receivers;
-            }
-            if (entry <= allReceivers.size())
-                targetObject = allReceivers.at(entry-1);
-        }
-        break;
-    default:
-        break;
-    }
-
-    *target = QAccessible::queryAccessibleInterface(targetObject);
-    return *target ? 0 : -1;
-}
-
-/*! \reimp */
 int QAccessibleWidget::childCount() const
 {
     QWidgetList cl = childWidgets(widget());
index d34d852..4b480ca 100644 (file)
@@ -61,14 +61,13 @@ public:
     QWindow *window() const;
     int childCount() const;
     int indexOfChild(const QAccessibleInterface *child) const;
-    QAccessible::Relation relationTo(const QAccessibleInterface *other) const;
+    QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > relations(QAccessible::Relation match = QAccessible::AllRelations) const;
     QAccessibleInterface *focusChild() const;
 
     QRect rect() const;
 
     QAccessibleInterface *parent() const;
     QAccessibleInterface *child(int index) const;
-    int navigate(QAccessible::RelationFlag rel, int entry, QAccessibleInterface **target) const;
 
     QString text(QAccessible::Text t) const;
     QAccessible::Role role() const;
index 63770f1..b2a4b1c 100644 (file)
@@ -846,6 +846,17 @@ public Q_SLOTS:
     }
 };
 
+static QAccessibleInterface *relatedInterface(QAccessibleInterface *iface, QAccessible::RelationFlag flag)
+{
+    typedef QPair<QAccessibleInterface *, QAccessible::Relation> RelationPair;
+    QVector<RelationPair> rels = iface->relations(flag);
+
+    for (int i = 1; i < rels.count(); ++i)
+        delete rels.at(i).first;
+
+    return rels.value(0).first;
+}
+
 void tst_QAccessibility::buttonTest()
 {
     QWidget window;
@@ -879,6 +890,30 @@ void tst_QAccessibility::buttonTest()
     toggletool.setText("Toggle");
     toggletool.setMinimumSize(20,20);
 
+    // test Controller/Controlled relations
+    {
+    QCheckBox toggler("Toggle me!", &window);
+    bool ok = connect(&pushButton, SIGNAL(clicked()), &toggler, SLOT(toggle()));
+    QCOMPARE(ok, true);
+    QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(&toggler);
+    QVERIFY(iface);
+    QCOMPARE(iface->role(), QAccessible::CheckBox);
+    QAccessibleInterface *buttonIFace = relatedInterface(iface, QAccessible::Controller);
+    QVERIFY(buttonIFace);
+    QCOMPARE(buttonIFace->role(), QAccessible::Button);
+    QCOMPARE(buttonIFace->object(), &pushButton);
+    delete buttonIFace;
+    delete iface;
+
+    buttonIFace = QAccessible::queryAccessibleInterface(&pushButton);
+    QVERIFY(buttonIFace);
+    QCOMPARE(buttonIFace->role(), QAccessible::Button);
+    iface = relatedInterface(buttonIFace, QAccessible::Controlled);
+    QVERIFY(iface);
+    QCOMPARE(iface->object(), &toggler);
+
+    }
+
     // test push button
     QAccessibleInterface* interface = QAccessible::queryAccessibleInterface(&pushButton);
     QAccessibleActionInterface* actionInterface = interface->actionInterface();