diff options
Diffstat (limited to 'src/gui/accessible/linux/atspiadaptor.cpp')
-rw-r--r-- | src/gui/accessible/linux/atspiadaptor.cpp | 552 |
1 files changed, 452 insertions, 100 deletions
diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp index 5fe0d54fe2..b3269a2a95 100644 --- a/src/gui/accessible/linux/atspiadaptor.cpp +++ b/src/gui/accessible/linux/atspiadaptor.cpp @@ -11,10 +11,12 @@ #include <qclipboard.h> #include <QtCore/qloggingcategory.h> +#include <QtCore/qtversion.h> #if QT_CONFIG(accessibility) #include "socket_interface.h" #include "qspi_constant_mappings_p.h" +#include <QtCore/private/qstringiterator_p.h> #include <QtGui/private/qaccessiblebridgeutils_p.h> #include "qspiapplicationadaptor_p.h" @@ -166,6 +168,9 @@ QString AtSpiAdaptor::introspect(const QString &path) const " <arg direction=\"out\" type=\"(so)\"/>\n" " <annotation value=\"QSpiObjectReference\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n" " </method>\n" + " <method name=\"GetAccessibleId\">\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " </method>\n" " </interface>\n" ); @@ -306,6 +311,39 @@ QString AtSpiAdaptor::introspect(const QString &path) const " </interface>\n" ); + static const QLatin1StringView selectionIntrospection( + " <interface name=\"org.a11y.atspi.Selection\">\n" + " <property name=\"NSelectedChildren\" type=\"i\" access=\"read\"/>\n" + " <method name=\"GetSelectedChild\">\n" + " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n" + " <arg direction=\"out\" type=\"(so)\"/>\n" + " <annotation name=\"org.qtproject.QtDBus.QtTypeName.Out0\" value=\"QSpiObjectReference\"/>\n" + " </method>\n" + " <method name=\"SelectChild\">\n" + " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"DeselectSelectedChild\">\n" + " <arg direction=\"in\" name=\"selectedChildIndex\" type=\"i\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"IsChildSelected\">\n" + " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"SelectAll\">\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"ClearSelection\">\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " <method name=\"DeselectChild\">\n" + " <arg direction=\"in\" name=\"childIndex\" type=\"i\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" + " </interface>\n" + ); + static const QLatin1StringView tableIntrospection( " <interface name=\"org.a11y.atspi.Table\">\n" " <property access=\"read\" type=\"i\" name=\"NRows\"/>\n" @@ -431,6 +469,14 @@ QString AtSpiAdaptor::introspect(const QString &path) const " <arg direction=\"out\" name=\"row_extents\" type=\"i\" />\n" " <arg direction=\"out\" name=\"col_extents\" type=\"i\" />\n" " </method>\n" + " <method name=\"GetColumnHeaderCells\">\n" + " <arg direction=\"out\" type=\"a(so)\"/>\n" + " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n" + " </method>\n" + " <method name=\"GetRowHeaderCells\">\n" + " <arg direction=\"out\" type=\"a(so)\"/>\n" + " <annotation value=\"QSpiObjectReferenceArray\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n" + " </method>\n" " </interface>\n" ); @@ -438,6 +484,13 @@ QString AtSpiAdaptor::introspect(const QString &path) const " <interface name=\"org.a11y.atspi.Text\">\n" " <property access=\"read\" type=\"i\" name=\"CharacterCount\"/>\n" " <property access=\"read\" type=\"i\" name=\"CaretOffset\"/>\n" + " <method name=\"GetStringAtOffset\">\n" + " <arg direction=\"in\" name=\"offset\" type=\"i\"/>\n" + " <arg direction=\"in\" name=\"granularity\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"s\"/>\n" + " <arg direction=\"out\" name=\"startOffset\" type=\"i\"/>\n" + " <arg direction=\"out\" name=\"endOffset\" type=\"i\"/>\n" + " </method>\n" " <method name=\"GetText\">\n" " <arg direction=\"in\" type=\"i\" name=\"startOffset\"/>\n" " <arg direction=\"in\" type=\"i\" name=\"endOffset\"/>\n" @@ -557,6 +610,12 @@ QString AtSpiAdaptor::introspect(const QString &path) const " <arg direction=\"out\" type=\"a{ss}\"/>\n" " <annotation value=\"QSpiAttributeSet\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n" " </method>\n" + " <method name=\"ScrollSubstringTo\">\n" + " <arg direction=\"in\" name=\"startOffset\" type=\"i\"/>\n" + " <arg direction=\"in\" name=\"endOffset\" type=\"i\"/>\n" + " <arg direction=\"in\" name=\"type\" type=\"u\"/>\n" + " <arg direction=\"out\" type=\"b\"/>\n" + " </method>\n" " </interface>\n" ); @@ -574,7 +633,7 @@ QString AtSpiAdaptor::introspect(const QString &path) const QAccessibleInterface * interface = interfaceFromPath(path); if (!interface) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << path; + qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << path; return QString(); } @@ -591,6 +650,8 @@ QString AtSpiAdaptor::introspect(const QString &path) const xml.append(editableTextIntrospection); if (interfaces.contains(ATSPI_DBUS_INTERFACE_ACTION ""_L1)) xml.append(actionIntrospection); + if (interfaces.contains(ATSPI_DBUS_INTERFACE_SELECTION ""_L1)) + xml.append(selectionIntrospection); if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE ""_L1)) xml.append(tableIntrospection); if (interfaces.contains(ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1)) @@ -685,7 +746,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag) || right.startsWith("VisibledataChanged"_L1)) { // typo in libatspi sendObject_visible_data_changed = 1; } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag; } } break; @@ -731,7 +792,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag) } else if (right.startsWith("DesktopDestroy"_L1)) { // ignore this one } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag; } } break; @@ -750,7 +811,7 @@ void AtSpiAdaptor::setBitFlag(const QString &flag) break; } default: - qCDebug(lcAccessibilityAtspi) << "WARNING: subscription string not handled:" << flag; + qCWarning(lcAccessibilityAtspi) << "Subscription string not handled:" << flag; } } @@ -903,8 +964,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::NameChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_name) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments("accessible-name"_L1, 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "NameChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + "accessible-name"_L1, 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "PropertyChange"_L1, args); } @@ -912,8 +982,17 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } case QAccessible::DescriptionChanged: { if (sendObject || sendObject_property_change || sendObject_property_change_accessible_description) { - QString path = pathForInterface(event->accessibleInterface()); - QVariantList args = packDBusSignalArguments("accessible-description"_L1, 0, 0, variantForPath(path)); + QAccessibleInterface *iface = event->accessibleInterface(); + if (!iface) { + qCDebug(lcAccessibilityAtspi, + "DescriptionChanged event from invalid accessible."); + return; + } + + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments( + "accessible-description"_L1, 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Description)))); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "PropertyChange"_L1, args); } @@ -930,7 +1009,7 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) if (sendObject || sendObject_text_changed) { QAccessibleInterface * iface = event->accessibleInterface(); if (!iface || !iface->textInterface()) { - qCDebug(lcAccessibilityAtspi) << "Received text event for invalid interface."; + qCWarning(lcAccessibilityAtspi) << "Received text event for invalid interface."; return; } QString path = pathForInterface(iface); @@ -962,14 +1041,14 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) if (!textRemoved.isEmpty()) { data.setVariant(QVariant::fromValue(textRemoved)); - QVariantList args = packDBusSignalArguments("delete"_L1, changePosition, textRemoved.length(), QVariant::fromValue(data)); + QVariantList args = packDBusSignalArguments("delete"_L1, changePosition, textRemoved.size(), QVariant::fromValue(data)); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "TextChanged"_L1, args); } if (!textInserted.isEmpty()) { data.setVariant(QVariant::fromValue(textInserted)); - QVariantList args = packDBusSignalArguments("insert"_L1, changePosition, textInserted.length(), QVariant::fromValue(data)); + QVariantList args = packDBusSignalArguments("insert"_L1, changePosition, textInserted.size(), QVariant::fromValue(data)); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "TextChanged"_L1, args); } @@ -1028,7 +1107,9 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) // Combo Box with AT-SPI likes to be special // It requires a name-change to update caches and then selection-changed QString path = pathForInterface(iface); - QVariantList args1 = packDBusSignalArguments("accessible-name"_L1, 0, 0, variantForPath(path)); + QVariantList args1 = packDBusSignalArguments( + "accessible-name"_L1, 0, 0, + QVariant::fromValue(QDBusVariant(iface->text(QAccessible::Name)))); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "PropertyChange"_L1, args1); QVariantList args2 = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(0)))); @@ -1048,13 +1129,37 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) qCWarning(lcAccessibilityAtspi) << "Selection event from invalid accessible."; return; } + // send event for change of selected state for the interface itself QString path = pathForInterface(iface); int selected = iface->state().selected ? 1 : 0; QVariantList stateArgs = packDBusSignalArguments("selected"_L1, selected, 0, variantForPath(path)); sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs); + + // send SelectionChanged event for the parent + QAccessibleInterface* parent = iface->parent(); + if (!parent) { + qCDebug(lcAccessibilityAtspi) << "No valid parent in selection event."; + return; + } + + QString parentPath = pathForInterface(parent); + QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(parentPath)); + sendDBusSignal(parentPath, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), + QLatin1String("SelectionChanged"), args); break; } + case QAccessible::SelectionWithin: { + QAccessibleInterface * iface = event->accessibleInterface(); + if (!iface) { + qCWarning(lcAccessibilityAtspi) << "SelectionWithin event from invalid accessible."; + return; + } + QString path = pathForInterface(iface); + QVariantList args = packDBusSignalArguments(QString(), 0, 0, variantForPath(path)); + sendDBusSignal(path, QLatin1String(ATSPI_DBUS_INTERFACE_EVENT_OBJECT), QLatin1String("SelectionChanged"), args); + break; + } case QAccessible::StateChanged: { if (sendObject || sendObject_state_changed || sendWindow || sendWindow_activate) { QAccessible::State stateChange = static_cast<QAccessibleStateChangeEvent*>(event)->changedStates(); @@ -1095,11 +1200,71 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) } break; } + case QAccessible::TableModelChanged: { + QAccessibleInterface *interface = event->accessibleInterface(); + if (!interface || !interface->isValid()) { + qCWarning(lcAccessibilityAtspi) << "TableModelChanged event from invalid accessible."; + return; + } + + const QString path = pathForInterface(interface); + QAccessibleTableModelChangeEvent *tableModelEvent = static_cast<QAccessibleTableModelChangeEvent*>(event); + switch (tableModelEvent->modelChangeType()) { + case QAccessibleTableModelChangeEvent::ColumnsInserted: { + if (sendObject || sendObject_column_inserted) { + const int firstColumn = tableModelEvent->firstColumn(); + const int insertedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1; + QVariantList args = packDBusSignalArguments(QString(), firstColumn, insertedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnInserted"_L1, args); + } + break; + } + case QAccessibleTableModelChangeEvent::ColumnsRemoved: { + if (sendObject || sendObject_column_deleted) { + const int firstColumn = tableModelEvent->firstColumn(); + const int removedColumnCount = tableModelEvent->lastColumn() - firstColumn + 1; + QVariantList args = packDBusSignalArguments(QString(), firstColumn, removedColumnCount, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ColumnDeleted"_L1, args); + } + break; + } + case QAccessibleTableModelChangeEvent::RowsInserted: { + if (sendObject || sendObject_row_inserted) { + const int firstRow = tableModelEvent->firstRow(); + const int insertedRowCount = tableModelEvent->lastRow() - firstRow + 1; + QVariantList args = packDBusSignalArguments(QString(), firstRow, insertedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowInserted"_L1, args); + } + break; + } + case QAccessibleTableModelChangeEvent::RowsRemoved: { + if (sendObject || sendObject_row_deleted) { + const int firstRow = tableModelEvent->firstRow(); + const int removedRowCount = tableModelEvent->lastRow() - firstRow + 1; + QVariantList args = packDBusSignalArguments(QString(), firstRow, removedRowCount, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "RowDeleted"_L1, args); + } + break; + } + case QAccessibleTableModelChangeEvent::ModelChangeType::ModelReset: { + if (sendObject || sendObject_model_changed) { + QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "ModelChanged"_L1, args); + } + break; + } + case QAccessibleTableModelChangeEvent::DataChanged: { + if (sendObject || sendObject_visible_data_changed) { + QVariantList args = packDBusSignalArguments(QString(), 0, 0, QVariant::fromValue(QDBusVariant(QVariant(QString())))); + sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "VisibleDataChanged"_L1, args); + } + break; + } + } + break; + } + // For now we ignore these events - case QAccessible::TableModelChanged: - // For tables, setting manages_descendants should - // indicate to the client that it cannot cache these - // interfaces. case QAccessible::ParentChanged: case QAccessible::DialogStart: case QAccessible::DialogEnd: @@ -1144,7 +1309,6 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event) case QAccessible::TextAttributeChanged: case QAccessible::TextColumnChanged: case QAccessible::VisibleDataChanged: - case QAccessible::SelectionWithin: case QAccessible::LocationChanged: case QAccessible::HelpChanged: case QAccessible::DefaultActionChanged: @@ -1238,11 +1402,11 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect // get accessible interface QAccessibleInterface * accessible = interfaceFromPath(message.path()); if (!accessible) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find accessible on path: " << message.path(); + qCWarning(lcAccessibilityAtspi) << "Could not find accessible on path:" << message.path(); return false; } if (!accessible->isValid()) { - qCWarning(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Accessible invalid: " << accessible << message.path(); + qCWarning(lcAccessibilityAtspi) << "Accessible invalid:" << accessible << message.path(); return false; } @@ -1272,6 +1436,8 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect return componentInterface(accessible, function, message, connection); if (interface == ATSPI_DBUS_INTERFACE_ACTION ""_L1) return actionInterface(accessible, function, message, connection); + if (interface == ATSPI_DBUS_INTERFACE_SELECTION ""_L1) + return selectionInterface(accessible, function, message, connection); if (interface == ATSPI_DBUS_INTERFACE_TEXT ""_L1) return textInterface(accessible, function, message, connection); if (interface == ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_L1) @@ -1291,7 +1457,7 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect bool AtSpiAdaptor::applicationInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { if (message.path() != ATSPI_DBUS_PATH_ROOT ""_L1) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find application interface for: " << message.path() << interface; + qCWarning(lcAccessibilityAtspi) << "Could not find application interface for:" << message.path() << interface; return false; } @@ -1343,13 +1509,33 @@ void AtSpiAdaptor::registerApplication() const QSpiObjectReference &socket = reply.value(); accessibilityRegistry = QSpiObjectReference(socket); } else { - qCDebug(lcAccessibilityAtspi) << "Error in contacting registry: " + qCWarning(lcAccessibilityAtspi) << "Error in contacting registry:" << reply.error().name() << reply.error().message(); } delete registry; } +namespace { +QString accessibleIdForAccessible(QAccessibleInterface *accessible) +{ + QString result; + while (accessible) { + if (!result.isEmpty()) + result.prepend(u'.'); + if (auto obj = accessible->object()) { + const QString name = obj->objectName(); + if (!name.isEmpty()) + result.prepend(name); + else + result.prepend(QString::fromUtf8(obj->metaObject()->className())); + } + accessible = accessible->parent(); + } + return result; +} +} // namespace + // Accessible bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { @@ -1403,6 +1589,9 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS } else if (function == "GetState"_L1) { quint64 spiState = spiStatesFromQState(interface->state()); if (interface->tableInterface()) { + // For tables, setting manages_descendants should + // indicate to the client that it cannot cache these + // interfaces. setSpiStateBit(&spiState, ATSPI_STATE_MANAGES_DESCENDANTS); } QAccessible::Role role = interface->role(); @@ -1433,8 +1622,11 @@ bool AtSpiAdaptor::accessibleInterface(QAccessibleInterface *interface, const QS children << ref; } connection.send(message.createReply(QVariant::fromValue(children))); + } else if (function == "GetAccessibleId"_L1) { + sendReply(connection, message, + QVariant::fromValue(QDBusVariant(accessibleIdForAccessible(interface)))); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::accessibleInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::accessibleInterface does not implement" << function << message.path(); return false; } return true; @@ -1451,7 +1643,7 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) { QStringList ifaces; qCDebug(lcAccessibilityAtspiCreation) << "AtSpiAdaptor::accessibleInterfaces create: " << interface->object(); - ifaces << ATSPI_DBUS_INTERFACE_ACCESSIBLE ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_ACCESSIBLE ""_s; if ( (!interface->rect().isEmpty()) || (interface->object() && interface->object()->isWidgetType()) || @@ -1461,30 +1653,33 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) (interface->role() == QAccessible::Row) || (interface->object() && interface->object()->inherits("QSGItem")) ) { - ifaces << ATSPI_DBUS_INTERFACE_COMPONENT ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_COMPONENT ""_s; } else { qCDebug(lcAccessibilityAtspiCreation) << " IS NOT a component"; } if (interface->role() == QAccessible::Application) - ifaces << ATSPI_DBUS_INTERFACE_APPLICATION ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_APPLICATION ""_s; if (interface->actionInterface() || interface->valueInterface()) - ifaces << ATSPI_DBUS_INTERFACE_ACTION ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_ACTION ""_s; + + if (interface->selectionInterface()) + ifaces << ATSPI_DBUS_INTERFACE_SELECTION ""_L1; if (interface->textInterface()) - ifaces << ATSPI_DBUS_INTERFACE_TEXT ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_TEXT ""_s; if (interface->editableTextInterface()) - ifaces << ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_EDITABLE_TEXT ""_s; if (interface->valueInterface()) - ifaces << ATSPI_DBUS_INTERFACE_VALUE ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_VALUE ""_s; if (interface->tableInterface()) - ifaces << ATSPI_DBUS_INTERFACE_TABLE ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE ""_s; if (interface->tableCellInterface()) - ifaces << ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1; + ifaces << u"" ATSPI_DBUS_INTERFACE_TABLE_CELL ""_s; return ifaces; } @@ -1521,7 +1716,7 @@ QString AtSpiAdaptor::pathForObject(QObject *object) const Q_ASSERT(object); if (inheritsQAction(object)) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: warning: creating path with QAction as object."; + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::pathForObject: Creating path with QAction as object."; } QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(object); @@ -1531,9 +1726,9 @@ QString AtSpiAdaptor::pathForObject(QObject *object) const QString AtSpiAdaptor::pathForInterface(QAccessibleInterface *interface) const { if (!interface || !interface->isValid()) - return ATSPI_DBUS_PATH_NULL ""_L1; + return u"" ATSPI_DBUS_PATH_NULL ""_s; if (interface->role() == QAccessible::Application) - return QSPI_OBJECT_PATH_ROOT ""_L1; + return u"" QSPI_OBJECT_PATH_ROOT ""_s; QAccessible::Id id = QAccessible::uniqueId(interface); Q_ASSERT((int)id < 0); @@ -1555,15 +1750,14 @@ bool AtSpiAdaptor::inheritsQAction(QObject *object) // Component static QAccessibleInterface * getWindow(QAccessibleInterface * interface) { - if (interface->role() == QAccessible::Dialog || interface->role() == QAccessible::Window) - return interface; - - QAccessibleInterface * parent = interface->parent(); - while (parent && parent->role() != QAccessible::Dialog - && parent->role() != QAccessible::Window) - parent = parent->parent(); - - return parent; + // find top-level window in a11y hierarchy (either has a + // corresponding role or is a direct child of the application object) + QAccessibleInterface* app = QAccessible::queryAccessibleInterface(qApp); + while (interface && interface->role() != QAccessible::Dialog + && interface->role() != QAccessible::Window && interface->parent() != app) + interface = interface->parent(); + + return interface; } bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) @@ -1650,7 +1844,7 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt qCDebug(lcAccessibilityAtspi) << "SetSize is not implemented."; sendReply(connection, message, false); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::componentInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::componentInterface does not implement" << function << message.path(); return false; } return true; @@ -1665,12 +1859,12 @@ QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType) bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { if (function == "GetNActions"_L1) { - int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count(); + int count = QAccessibleBridgeUtils::effectiveActionNames(interface).size(); sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count)))); } else if (function == "DoAction"_L1) { int index = message.arguments().at(0).toInt(); const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) + if (index < 0 || index >= actionNames.size()) return false; const QString actionName = actionNames.at(index); bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName); @@ -1680,13 +1874,13 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin } else if (function == "GetName"_L1) { int index = message.arguments().at(0).toInt(); const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) + if (index < 0 || index >= actionNames.size()) return false; sendReply(connection, message, actionNames.at(index)); } else if (function == "GetDescription"_L1) { int index = message.arguments().at(0).toInt(); const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) + if (index < 0 || index >= actionNames.size()) return false; QString description; if (QAccessibleActionInterface *actionIface = interface->actionInterface()) @@ -1697,7 +1891,7 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin } else if (function == "GetKeyBinding"_L1) { int index = message.arguments().at(0).toInt(); const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); - if (index < 0 || index >= actionNames.count()) + if (index < 0 || index >= actionNames.size()) return false; QStringList keyBindings; if (QAccessibleActionInterface *actionIface = interface->actionInterface()) @@ -1707,12 +1901,12 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin if (!acc.isEmpty()) keyBindings.append(acc); } - if (keyBindings.length() > 0) + if (keyBindings.size() > 0) sendReply(connection, message, keyBindings.join(u';')); else sendReply(connection, message, QString()); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::actionInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::actionInterface does not implement" << function << message.path(); return false; } return true; @@ -1794,8 +1988,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString int offset = message.arguments().at(0).toInt(); int start; int end; - QString result = interface->textInterface()->textAtOffset(offset, QAccessible::CharBoundary, &start, &end); - sendReply(connection, message, (int) *(qPrintable (result))); + const QString charString = interface->textInterface() + ->textAtOffset(offset, QAccessible::CharBoundary, &start, &end); + int codePoint = 0; + QStringIterator stringIt(charString); + if (stringIt.hasNext()) + codePoint = static_cast<int>(stringIt.peekNext()); + sendReply(connection, message, codePoint); } else if (function == "GetCharacterExtents"_L1) { int offset = message.arguments().at(0).toInt(); int coordType = message.arguments().at(1).toUInt(); @@ -1830,6 +2029,16 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString QVariantList sel; sel << start << end; connection.send(message.createReply(sel)); + } else if (function == "GetStringAtOffset"_L1) { + int offset = message.arguments().at(0).toInt(); + uint granularity = message.arguments().at(1).toUInt(); + if (!isValidAtspiTextGranularity(granularity)) + return false; + int startOffset, endOffset; + QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiTextGranularity(granularity), &startOffset, &endOffset); + QVariantList ret; + ret << text << startOffset << endOffset; + connection.send(message.createReply(ret)); } else if (function == "GetText"_L1) { int startOffset = message.arguments().at(0).toInt(); int endOffset = message.arguments().at(1).toInt(); @@ -1840,7 +2049,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString int offset = message.arguments().at(0).toInt(); int type = message.arguments().at(1).toUInt(); int startOffset, endOffset; - QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QString text = interface->textInterface()->textAfterOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset); QVariantList ret; ret << text << startOffset << endOffset; connection.send(message.createReply(ret)); @@ -1848,7 +2057,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString int offset = message.arguments().at(0).toInt(); int type = message.arguments().at(1).toUInt(); int startOffset, endOffset; - QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QString text = interface->textInterface()->textAtOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset); QVariantList ret; ret << text << startOffset << endOffset; connection.send(message.createReply(ret)); @@ -1856,7 +2065,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString int offset = message.arguments().at(0).toInt(); int type = message.arguments().at(1).toUInt(); int startOffset, endOffset; - QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryType(type), &startOffset, &endOffset); + QString text = interface->textInterface()->textBeforeOffset(offset, qAccessibleBoundaryTypeFromAtspiBoundaryType(type), &startOffset, &endOffset); QVariantList ret; ret << text << startOffset << endOffset; connection.send(message.createReply(ret)); @@ -1868,6 +2077,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString int offset = message.arguments().at(0).toInt(); interface->textInterface()->setCursorPosition(offset); sendReply(connection, message, true); + } else if (function == "ScrollSubstringTo"_L1) { + int startOffset = message.arguments().at(0).toInt(); + int endOffset = message.arguments().at(1).toInt(); + // ignore third parameter (scroll type), since QAccessibleTextInterface::scrollToSubstring doesn't have that + qCInfo(lcAccessibilityAtspi) << "AtSpiAdaptor::ScrollSubstringTo doesn'take take scroll type into account."; + interface->textInterface()->scrollToSubstring(startOffset, endOffset); + sendReply(connection, message, true); } else if (function == "SetSelection"_L1) { int selectionNum = message.arguments().at(0).toInt(); int startOffset = message.arguments().at(1).toInt(); @@ -1875,13 +2091,13 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString interface->textInterface()->setSelection(selectionNum, startOffset, endOffset); sendReply(connection, message, true); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::textInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::textInterface does not implement" << function << message.path(); return false; } return true; } -QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTextBoundaryType) const +QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType) { switch (atspiTextBoundaryType) { case ATSPI_TEXT_BOUNDARY_CHAR: @@ -1900,6 +2116,38 @@ QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryType(int atspiTex return QAccessible::CharBoundary; } +bool AtSpiAdaptor::isValidAtspiTextGranularity(uint atspiTextGranularity) +{ + if (atspiTextGranularity == ATSPI_TEXT_GRANULARITY_CHAR + || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_WORD + || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_SENTENCE + || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_LINE + || atspiTextGranularity == ATSPI_TEXT_GRANULARITY_PARAGRAPH) + return true; + + qCWarning(lcAccessibilityAtspi) << "Unknown value" << atspiTextGranularity << "for AT-SPI text granularity type"; + return false; +} + +QAccessible::TextBoundaryType AtSpiAdaptor::qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity) +{ + Q_ASSERT(isValidAtspiTextGranularity(atspiTextGranularity)); + + switch (atspiTextGranularity) { + case ATSPI_TEXT_GRANULARITY_CHAR: + return QAccessible::CharBoundary; + case ATSPI_TEXT_GRANULARITY_WORD: + return QAccessible::WordBoundary; + case ATSPI_TEXT_GRANULARITY_SENTENCE: + return QAccessible::SentenceBoundary; + case ATSPI_TEXT_GRANULARITY_LINE: + return QAccessible::LineBoundary; + case ATSPI_TEXT_GRANULARITY_PARAGRAPH: + return QAccessible::ParagraphBoundary; + } + return QAccessible::CharBoundary; +} + namespace { struct AtSpiAttribute { @@ -1912,13 +2160,13 @@ namespace QString atspiColor(const QString &ia2Color) { // "rgb(%u,%u,%u)" -> "%u,%u,%u" - return ia2Color.mid(4, ia2Color.length() - (4+1)); + return ia2Color.mid(4, ia2Color.size() - (4+1)).replace(u"\\,"_s, u","_s); } QString atspiSize(const QString &ia2Size) { // "%fpt" -> "%f" - return ia2Size.left(ia2Size.length() - 2); + return ia2Size.left(ia2Size.size() - 2); } AtSpiAttribute atspiTextAttribute(const QString &ia2Name, const QString &ia2Value) @@ -1926,9 +2174,9 @@ namespace QString name = ia2Name; QString value = ia2Value; - // IAccessible2: http://www.linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes - // ATK attribute names: https://git.gnome.org/browse/orca/tree/src/orca/text_attribute_names.py - // ATK attribute values: https://developer.gnome.org/atk/unstable/AtkText.html#AtkTextAttribute + // IAccessible2: https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes + // ATK attribute names: https://gitlab.gnome.org/GNOME/orca/-/blob/master/src/orca/text_attribute_names.py + // ATK attribute values: https://gnome.pages.gitlab.gnome.org/atk/AtkText.html#AtkTextAttribute // https://bugzilla.gnome.org/show_bug.cgi?id=744553 "ATK docs provide no guidance for allowed values of some text attributes" // specifically for "weight", "invalid", "language" and value range for colors @@ -1974,6 +2222,13 @@ namespace // (on which it produces traceback and fails to read any following text attributes), // but that is the default value, so omit it anyway value = QString(); + } else if (((ia2Name == "text-line-through-style"_L1 || ia2Name == "text-line-through-type"_L1) && (ia2Value != "none"_L1)) + || (ia2Name == "text-line-through-text"_L1 && !ia2Value.isEmpty())) { + // if any of the above is set, set "strikethrough" to true, but don't explicitly set + // to false otherwise, since any of the others might still be set to indicate strikethrough + // and no strikethrough is assumed anyway when nothing is explicitly set + name = QStringLiteral("strikethrough"); + value = QStringLiteral("true"); } else if (ia2Name == "text-position"_L1) { name = QStringLiteral("vertical-align"); if (value != "baseline"_L1 && value != "super"_L1 && value != "sub"_L1) { @@ -2021,11 +2276,13 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of QString joined = interface->textInterface()->attributes(offset, &startOffset, &endOffset); const QStringList attributes = joined.split(u';', Qt::SkipEmptyParts, Qt::CaseSensitive); for (const QString &attr : attributes) { - QStringList items; - items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive); - AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); - if (!attribute.isNull()) - set[attribute.name] = attribute.value; + QStringList items = attr.split(u':', Qt::SkipEmptyParts, Qt::CaseSensitive); + if (items.count() == 2) + { + AtSpiAttribute attribute = atspiTextAttribute(items[0], items[1]); + if (!attribute.isNull()) + set[attribute.name] = attribute.value; + } } QVariantList list; @@ -2083,7 +2340,7 @@ bool AtSpiAdaptor::isValidCoordType(uint coordType) if (coordType == ATSPI_COORD_TYPE_SCREEN || coordType == ATSPI_COORD_TYPE_WINDOW || coordType == ATSPI_COORD_TYPE_PARENT) return true; - qCWarning(lcAccessibilityAtspi) << "unknown value" << coordType << "for AT-SPI coord type"; + qCWarning(lcAccessibilityAtspi) << "Unknown value" << coordType << "for AT-SPI coord type"; return false; } @@ -2131,7 +2388,7 @@ static QString textForRange(QAccessibleInterface *accessible, int startOffset, i } QString txt = accessible->text(QAccessible::Value); if (endOffset == -1) - endOffset = txt.length(); + endOffset = txt.size(); return txt.mid(startOffset, endOffset - startOffset); } @@ -2139,7 +2396,7 @@ static void replaceTextFallback(QAccessibleInterface *accessible, long startOffs { QString t = textForRange(accessible, 0, -1); if (endOffset == -1) - endOffset = t.length(); + endOffset = t.size(); if (endOffset - startOffset == 0) t.insert(startOffset, txt); else @@ -2204,10 +2461,10 @@ bool AtSpiAdaptor::editableTextInterface(QAccessibleInterface *interface, const else replaceTextFallback(interface, 0, -1, newContents); connection.send(message.createReply(true)); - } else if (function == ""_L1) { + } else if (function.isEmpty()) { connection.send(message.createReply()); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::editableTextInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::editableTextInterface does not implement" << function << message.path(); return false; } return true; @@ -2226,7 +2483,7 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString //Temporary fix //See https://bugzilla.gnome.org/show_bug.cgi?id=652596 valueIface->setCurrentValue(value); - connection.send(message.createReply()); // FIXME is the reply needed? + connection.send(message.createReply()); } else { QVariant value; if (function == "GetCurrentValue"_L1) @@ -2238,11 +2495,11 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString else if (function == "GetMinimumValue"_L1) value = valueIface->minimumValue(); else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::valueInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface does not implement" << function << message.path(); return false; } if (!value.canConvert<double>()) { - qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double: " << function; + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::valueInterface: Could not convert to double:" << function; } // explicitly convert to dbus-variant containing one double since atspi expects that @@ -2253,11 +2510,68 @@ bool AtSpiAdaptor::valueInterface(QAccessibleInterface *interface, const QString return true; } +// Selection interface +bool AtSpiAdaptor::selectionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) +{ + QAccessibleSelectionInterface* selectionInterface = interface->selectionInterface(); + if (!selectionInterface) { + qCWarning(lcAccessibilityAtspi) << "Could not find selection interface for: " << message.path() << interface; + return false; + } + + if (function == "ClearSelection"_L1 ) { + connection.send(message.createReply(QVariant::fromValue((selectionInterface->clear())))); + } else if (function == "DeselectChild"_L1 ) { + int childIndex = message.arguments().at(0).toInt(); + bool ret = false; + QAccessibleInterface *child = interface->child(childIndex); + if (child) + ret = selectionInterface->unselect(child); + connection.send(message.createReply(QVariant::fromValue(ret))); + } else if (function == "DeselectSelectedChild"_L1 ) { + int selectionIndex = message.arguments().at(0).toInt(); + bool ret = false; + QAccessibleInterface *selectedChild = selectionInterface->selectedItem(selectionIndex); + if (selectedChild) + ret = selectionInterface->unselect(selectedChild); + connection.send(message.createReply(QVariant::fromValue(ret))); + } else if (function == "GetNSelectedChildren"_L1) { + connection.send(message.createReply(QVariant::fromValue(QDBusVariant( + QVariant::fromValue(selectionInterface->selectedItemCount()))))); + } else if (function == "GetSelectedChild"_L1) { + int selectionIndex = message.arguments().at(0).toInt(); + QSpiObjectReference ref(connection, QDBusObjectPath(pathForInterface(selectionInterface->selectedItem(selectionIndex)))); + connection.send(message.createReply(QVariant::fromValue(ref))); + } else if (function == "IsChildSelected"_L1 ) { + int childIndex = message.arguments().at(0).toInt(); + bool ret = false; + QAccessibleInterface *child = interface->child(childIndex); + if (child) + ret = selectionInterface->isSelected(child); + connection.send(message.createReply(QVariant::fromValue(ret))); + } else if (function == "SelectAll"_L1 ) { + connection.send(message.createReply(QVariant::fromValue(selectionInterface->selectAll()))); + } else if (function == "SelectChild"_L1 ) { + int childIndex = message.arguments().at(0).toInt(); + bool ret = false; + QAccessibleInterface *child = interface->child(childIndex); + if (child) + ret = selectionInterface->select(child); + connection.send(message.createReply(QVariant::fromValue(ret))); + } else { + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::selectionInterface does not implement " << function << message.path(); + return false; + } + + return true; +} + + // Table interface bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { if (!(interface->tableInterface() || interface->tableCellInterface())) { - qCDebug(lcAccessibilityAtspi) << "WARNING Qt AtSpiAdaptor: Could not find table interface for: " << message.path() << interface; + qCWarning(lcAccessibilityAtspi) << "Qt AtSpiAdaptor: Could not find table interface for:" << message.path() << interface; return false; } @@ -2293,7 +2607,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString (column < 0) || (row >= interface->tableInterface()->rowCount()) || (column >= interface->tableInterface()->columnCount())) { - qCDebug(lcAccessibilityAtspi) << "WARNING: invalid index for tableInterface GetAccessibleAt (" << row << ", " << column << ')'; + qCWarning(lcAccessibilityAtspi) << "Invalid index for tableInterface GetAccessibleAt (" << row << "," << column << ')'; return false; } @@ -2302,7 +2616,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString if (cell) { ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(cell))); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: no cell interface returned for " << interface->object() << row << column; + qCWarning(lcAccessibilityAtspi) << "No cell interface returned for" << interface->object() << row << column; ref = QSpiObjectReference(); } connection.send(message.createReply(QVariant::fromValue(ref))); @@ -2312,7 +2626,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString int column = message.arguments().at(1).toInt(); QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column); if (!cell) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell. " << interface; + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::GetIndexAt(" << row << ',' << column << ") did not find a cell." << interface; return false; } int index = interface->indexOfChild(cell); @@ -2332,7 +2646,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString ret = -1; } else { if (!cell->tableCellInterface()) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; + qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell; return false; } ret = cell->tableCellInterface()->columnIndex(); @@ -2344,14 +2658,14 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString ret = index % interface->tableInterface()->columnCount(); } else { if (!cell->tableCellInterface()) { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No table cell interface: " << cell; + qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No table cell interface: " << cell; return false; } ret = cell->tableCellInterface()->rowIndex(); } } } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::" << function << " No cell at index: " << index << interface; + qCWarning(lcAccessibilityAtspi).nospace() << "AtSpiAdaptor::" << function << " No cell at index: " << index << " " << interface; return false; } } @@ -2380,14 +2694,15 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString if (cols > 0) { row = index / cols; col = index % cols; - QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, col)->tableCellInterface(); - if (cell) { - row = cell->rowIndex(); - col = cell->columnIndex(); - rowExtents = cell->rowExtent(); - colExtents = cell->columnExtent(); - isSelected = cell->isSelected(); - success = true; + if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, col)) { + if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface()) { + row = cellIface->rowIndex(); + col = cellIface->columnIndex(); + rowExtents = cellIface->rowExtent(); + colExtents = cellIface->columnExtent(); + isSelected = cellIface->isSelected(); + success = true; + } } } QVariantList list; @@ -2397,12 +2712,22 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString } else if (function == "GetColumnExtentAt"_L1) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->columnExtent())); + int columnExtent = 0; + if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column)) { + if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface()) + columnExtent = cellIface->columnExtent(); + } + connection.send(message.createReply(columnExtent)); } else if (function == "GetRowExtentAt"_L1) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - connection.send(message.createReply(interface->tableInterface()->cellAt(row, column)->tableCellInterface()->rowExtent())); + int rowExtent = 0; + if (QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, column)) { + if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface()) + rowExtent = cellIface->rowExtent(); + } + connection.send(message.createReply(rowExtent)); } else if (function == "GetColumnHeader"_L1) { int column = message.arguments().at(0).toInt(); @@ -2420,9 +2745,9 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString } else if (function == "GetRowHeader"_L1) { int row = message.arguments().at(0).toInt(); QSpiObjectReference ref; - QAccessibleTableCellInterface *cell = interface->tableInterface()->cellAt(row, 0)->tableCellInterface(); - if (cell) { - QList<QAccessibleInterface*> header = cell->rowHeaderCells(); + QAccessibleInterface *cell = interface->tableInterface()->cellAt(row, 0); + if (cell && cell->tableCellInterface()) { + QList<QAccessibleInterface*> header = cell->tableCellInterface()->rowHeaderCells(); if (header.size() > 0) { ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(header.takeAt(0)))); } @@ -2442,8 +2767,12 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString } else if (function == "IsSelected"_L1) { int row = message.arguments().at(0).toInt(); int column = message.arguments().at(1).toInt(); - QAccessibleTableCellInterface* cell = interface->tableInterface()->cellAt(row, column)->tableCellInterface(); - connection.send(message.createReply(cell->isSelected())); + bool selected = false; + if (QAccessibleInterface* cell = interface->tableInterface()->cellAt(row, column)) { + if (QAccessibleTableCellInterface *cellIface = cell->tableCellInterface()) + selected = cellIface->isSelected(); + } + connection.send(message.createReply(selected)); } else if (function == "AddColumnSelection"_L1) { int column = message.arguments().at(0).toInt(); connection.send(message.createReply(interface->tableInterface()->selectColumn(column))); @@ -2457,7 +2786,7 @@ bool AtSpiAdaptor::tableInterface(QAccessibleInterface *interface, const QString int row = message.arguments().at(0).toInt(); connection.send(message.createReply(interface->tableInterface()->unselectRow(row))); } else { - qCDebug(lcAccessibilityAtspi) << "WARNING: AtSpiAdaptor::tableInterface does not implement " << function << message.path(); + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableInterface does not implement" << function << message.path(); return false; } return true; @@ -2472,7 +2801,17 @@ bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QSt return false; } - if (function == "GetColumnSpan"_L1) { + if (function == "GetColumnHeaderCells"_L1) { + QSpiObjectReferenceArray headerCells; + const auto headerCellInterfaces = cellInterface->columnHeaderCells(); + headerCells.reserve(headerCellInterfaces.size()); + for (QAccessibleInterface *cell : headerCellInterfaces) { + const QString childPath = pathForInterface(cell); + const QSpiObjectReference ref(connection, QDBusObjectPath(childPath)); + headerCells << ref; + } + connection.send(message.createReply(QVariant::fromValue(headerCells))); + } else if (function == "GetColumnSpan"_L1) { connection.send(message.createReply(QVariant::fromValue(QDBusVariant( QVariant::fromValue(cellInterface->columnExtent()))))); } else if (function == "GetPosition"_L1) { @@ -2480,6 +2819,16 @@ bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QSt const int column = cellInterface->columnIndex(); connection.send(message.createReply(QVariant::fromValue(QDBusVariant( QVariant::fromValue(QPoint(row, column)))))); + } else if (function == "GetRowHeaderCells"_L1) { + QSpiObjectReferenceArray headerCells; + const auto headerCellInterfaces = cellInterface->rowHeaderCells(); + headerCells.reserve(headerCellInterfaces.size()); + for (QAccessibleInterface *cell : headerCellInterfaces) { + const QString childPath = pathForInterface(cell); + const QSpiObjectReference ref(connection, QDBusObjectPath(childPath)); + headerCells << ref; + } + connection.send(message.createReply(QVariant::fromValue(headerCells))); } else if (function == "GetRowSpan"_L1) { connection.send(message.createReply(QVariant::fromValue(QDBusVariant( QVariant::fromValue(cellInterface->rowExtent()))))); @@ -2493,6 +2842,9 @@ bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QSt if (table && table->tableInterface()) ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(table))); connection.send(message.createReply(QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref))))); + } else { + qCWarning(lcAccessibilityAtspi) << "AtSpiAdaptor::tableCellInterface does not implement" << function << message.path(); + return false; } return true; |