summaryrefslogtreecommitdiffstats
path: root/src/gui/accessible/linux
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/accessible/linux')
-rw-r--r--src/gui/accessible/linux/atspiadaptor.cpp758
-rw-r--r--src/gui/accessible/linux/atspiadaptor_p.h16
-rw-r--r--src/gui/accessible/linux/dbusconnection.cpp12
-rw-r--r--src/gui/accessible/linux/dbusxml/Socket.xml2
-rw-r--r--src/gui/accessible/linux/qspi_constant_mappings.cpp14
-rw-r--r--src/gui/accessible/linux/qspiaccessiblebridge.cpp12
-rw-r--r--src/gui/accessible/linux/qspiapplicationadaptor.cpp18
7 files changed, 642 insertions, 190 deletions
diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp
index 426be1203e..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"
@@ -28,6 +30,11 @@
It sends notifications coming from Qt via dbus and listens to incoming dbus requests.
*/
+// ATSPI_COORD_TYPE_PARENT was added in at-spi 2.30, define here for older versions
+#if ATSPI_COORD_TYPE_COUNT < 3
+#define ATSPI_COORD_TYPE_PARENT 2
+#endif
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -161,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"
);
@@ -301,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"
@@ -409,10 +452,45 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" </interface>\n"
);
+ static const QLatin1StringView tableCellIntrospection(
+ " <interface name=\"org.a11y.atspi.TableCell\">\n"
+ " <property access=\"read\" name=\"ColumnSpan\" type=\"i\" />\n"
+ " <property access=\"read\" name=\"Position\" type=\"(ii)\">\n"
+ " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QPoint\"/>\n"
+ " </property>\n"
+ " <property access=\"read\" name=\"RowSpan\" type=\"i\" />\n"
+ " <property access=\"read\" name=\"Table\" type=\"(so)\" >\n"
+ " <annotation name=\"org.qtproject.QtDBus.QtTypeName\" value=\"QSpiObjectReference\"/>\n"
+ " </property>\n"
+ " <method name=\"GetRowColumnSpan\">\n"
+ " <arg direction=\"out\" type=\"b\" />\n"
+ " <arg direction=\"out\" name=\"row\" type=\"i\" />\n"
+ " <arg direction=\"out\" name=\"col\" type=\"i\" />\n"
+ " <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"
+ );
+
static const QLatin1StringView textIntrospection(
" <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"
@@ -451,9 +529,6 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
" <arg direction=\"in\" type=\"s\" name=\"attributeName\"/>\n"
" <arg direction=\"out\" type=\"s\"/>\n"
- " <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
- " <arg direction=\"out\" type=\"i\" name=\"endOffset\"/>\n"
- " <arg direction=\"out\" type=\"b\" name=\"defined\"/>\n"
" </method>\n"
" <method name=\"GetAttributes\">\n"
" <arg direction=\"in\" type=\"i\" name=\"offset\"/>\n"
@@ -482,6 +557,7 @@ QString AtSpiAdaptor::introspect(const QString &path) const
" </method>\n"
" <method name=\"GetNSelections\">\n"
" <arg direction=\"out\" type=\"i\"/>\n"
+ " </method>\n"
" <method name=\"GetSelection\">\n"
" <arg direction=\"in\" type=\"i\" name=\"selectionNum\"/>\n"
" <arg direction=\"out\" type=\"i\" name=\"startOffset\"/>\n"
@@ -534,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"
);
@@ -551,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();
}
@@ -568,8 +650,12 @@ 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))
+ xml.append(tableCellIntrospection);
if (interfaces.contains(ATSPI_DBUS_INTERFACE_VALUE ""_L1))
xml.append(valueIntrospection);
if (path == QSPI_OBJECT_PATH_ROOT ""_L1)
@@ -660,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;
@@ -706,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;
@@ -725,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;
}
}
@@ -878,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);
}
@@ -887,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);
}
@@ -905,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);
@@ -937,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);
}
@@ -1003,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))));
@@ -1023,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();
@@ -1061,15 +1191,80 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
notifyStateChange(iface, "enabled"_L1, enabled);
notifyStateChange(iface, "sensitive"_L1, enabled);
+ } else if (stateChange.focused) {
+ QAccessibleInterface *iface = event->accessibleInterface();
+ QAccessible::State state = iface->state();
+ bool focused = state.focused;
+ notifyStateChange(iface, "focused"_L1, focused);
}
}
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:
@@ -1114,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:
@@ -1160,9 +1354,6 @@ void AtSpiAdaptor::childrenChanged(QAccessibleInterface *interface) const
void AtSpiAdaptor::notifyAboutCreation(QAccessibleInterface *interface) const
{
-// // say hello to d-bus
-// cache->emitAddAccessible(accessible->getCacheItem());
-
// notify about the new child of our parent
QAccessibleInterface * parent = interface->parent();
if (!parent) {
@@ -1211,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;
}
@@ -1245,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)
@@ -1253,6 +1446,8 @@ bool AtSpiAdaptor::handleMessage(const QDBusMessage &message, const QDBusConnect
return valueInterface(accessible, function, message, connection);
if (interface == ATSPI_DBUS_INTERFACE_TABLE ""_L1)
return tableInterface(accessible, function, message, connection);
+ if (interface == ATSPI_DBUS_INTERFACE_TABLE_CELL ""_L1)
+ return tableCellInterface(accessible, function, message, connection);
qCDebug(lcAccessibilityAtspi) << "AtSpiAdaptor::handleMessage with unknown interface: " << message.path() << interface << function;
return false;
@@ -1262,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;
}
@@ -1314,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)
{
@@ -1374,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();
@@ -1404,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;
@@ -1422,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()) ||
@@ -1432,27 +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 << u"" ATSPI_DBUS_INTERFACE_TABLE_CELL ""_s;
return ifaces;
}
@@ -1489,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);
@@ -1499,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);
@@ -1523,31 +1750,14 @@ bool AtSpiAdaptor::inheritsQAction(QObject *object)
// Component
static QAccessibleInterface * getWindow(QAccessibleInterface * interface)
{
- if (interface->role() == QAccessible::Window)
- return interface;
-
- QAccessibleInterface * parent = interface->parent();
- while (parent && parent->role() != QAccessible::Window)
- parent = parent->parent();
-
- return parent;
-}
-
-static QRect getRelativeRect(QAccessibleInterface *interface)
-{
- QAccessibleInterface * window;
- QRect wr, cr;
-
- cr = interface->rect();
-
- window = getWindow(interface);
- if (window) {
- wr = window->rect();
-
- cr.setX(cr.x() - wr.x());
- cr.setY(cr.x() - wr.y());
- }
- return cr;
+ // 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)
@@ -1557,28 +1767,22 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
int x = message.arguments().at(0).toInt();
int y = message.arguments().at(1).toInt();
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_SCREEN)
- ret = interface->rect().contains(x, y);
- else
- ret = getRelativeRect(interface).contains(x, y);
+ if (!isValidCoordType(coordType))
+ return false;
+ ret = getExtents(interface, coordType).contains(x, y);
sendReply(connection, message, ret);
} else if (function == "GetAccessibleAtPoint"_L1) {
- int x = message.arguments().at(0).toInt();
- int y = message.arguments().at(1).toInt();
+ QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_WINDOW) {
- QWindow * window = interface->window();
- if (window) {
- x += window->position().x();
- y += window->position().y();
- }
- }
+ if (!isValidCoordType(coordType))
+ return false;
+ QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
- QAccessibleInterface * childInterface(interface->childAt(x, y));
+ QAccessibleInterface * childInterface(interface->childAt(screenPos.x(), screenPos.y()));
QAccessibleInterface * iface = nullptr;
while (childInterface) {
iface = childInterface;
- childInterface = iface->childAt(x, y);
+ childInterface = iface->childAt(screenPos.x(), screenPos.y());
}
if (iface) {
QString path = pathForInterface(iface);
@@ -1592,6 +1796,8 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, (double) 1.0);
} else if (function == "GetExtents"_L1) {
uint coordType = message.arguments().at(0).toUInt();
+ if (!isValidCoordType(coordType))
+ return false;
sendReply(connection, message, QVariant::fromValue(getExtents(interface, coordType)));
} else if (function == "GetLayer"_L1) {
sendReply(connection, message, QVariant::fromValue((uint)1));
@@ -1599,11 +1805,9 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
sendReply(connection, message, QVariant::fromValue((short)0));
} else if (function == "GetPosition"_L1) {
uint coordType = message.arguments().at(0).toUInt();
- QRect rect;
- if (coordType == ATSPI_COORD_TYPE_SCREEN)
- rect = interface->rect();
- else
- rect = getRelativeRect(interface);
+ if (!isValidCoordType(coordType))
+ return false;
+ QRect rect = getExtents(interface, coordType);
QVariantList pos;
pos << rect.x() << rect.y();
connection.send(message.createReply(pos));
@@ -1640,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;
@@ -1648,19 +1852,19 @@ bool AtSpiAdaptor::componentInterface(QAccessibleInterface *interface, const QSt
QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType)
{
- return (coordType == ATSPI_COORD_TYPE_SCREEN) ? interface->rect() : getRelativeRect(interface);
+ return translateFromScreenCoordinates(interface, interface->rect(), coordType);
}
// Action interface
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);
@@ -1670,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())
@@ -1687,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())
@@ -1697,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;
@@ -1759,7 +1963,7 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
} else if (function == "GetAttributeValue"_L1) {
int offset = message.arguments().at(0).toInt();
QString attributeName = message.arguments().at(1).toString();
- connection.send(message.createReply(getAttributeValue(interface, offset, attributeName)));
+ connection.send(message.createReply(QVariant(getAttributeValue(interface, offset, attributeName))));
} else if (function == "GetAttributes"_L1) {
int offset = message.arguments().at(0).toInt();
connection.send(message.createReply(getAttributes(interface, offset, true)));
@@ -1784,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();
@@ -1801,11 +2010,10 @@ bool AtSpiAdaptor::textInterface(QAccessibleInterface *interface, const QString
Q_ASSERT(!message.signature().isEmpty());
QPoint point(message.arguments().at(0).toInt(), message.arguments().at(1).toInt());
uint coordType = message.arguments().at(2).toUInt();
- if (coordType == ATSPI_COORD_TYPE_WINDOW) {
- QWindow *win = interface->window();
- point -= QPoint(win->x(), win->y());
- }
- int offset = interface->textInterface()->offsetAtPoint(point);
+ if (!isValidCoordType(coordType))
+ return false;
+ QPoint screenPos = translateToScreenCoordinates(interface, point, coordType);
+ int offset = interface->textInterface()->offsetAtPoint(screenPos);
sendReply(connection, message, offset);
} else if (function == "GetRangeExtents"_L1) {
int startOffset = message.arguments().at(0).toInt();
@@ -1821,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();
@@ -1831,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));
@@ -1839,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));
@@ -1847,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));
@@ -1859,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();
@@ -1866,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:
@@ -1891,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 {
@@ -1903,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)
@@ -1917,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
@@ -1965,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) {
@@ -2012,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;
@@ -2025,9 +2291,8 @@ QVariantList AtSpiAdaptor::getAttributes(QAccessibleInterface *interface, int of
return list;
}
-QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
+QString AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, int offset, const QString &attributeName) const
{
- QString mapped;
QString joined;
QSpiAttributeSet map;
int startOffset;
@@ -2042,20 +2307,13 @@ QVariantList AtSpiAdaptor::getAttributeValue(QAccessibleInterface *interface, in
if (!attribute.isNull())
map[attribute.name] = attribute.value;
}
- mapped = map[attributeName];
- const bool defined = !mapped.isEmpty();
- QVariantList list;
- list << mapped << startOffset << endOffset << defined;
- return list;
+ return map[attributeName];
}
QList<QVariant> AtSpiAdaptor::getCharacterExtents(QAccessibleInterface *interface, int offset, uint coordType) const
{
QRect rect = interface->textInterface()->characterRect(offset);
-
- if (coordType == ATSPI_COORD_TYPE_WINDOW)
- rect = translateRectToWindowCoordinates(interface, rect);
-
+ rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
@@ -2073,22 +2331,52 @@ QList<QVariant> AtSpiAdaptor::getRangeExtents(QAccessibleInterface *interface,
for (int i=startOffset + 1; i <= endOffset; i++)
rect = rect | textInterface->characterRect(i);
- // relative to window
- if (coordType == ATSPI_COORD_TYPE_WINDOW)
- rect = translateRectToWindowCoordinates(interface, rect);
-
+ rect = translateFromScreenCoordinates(interface, rect, coordType);
return QList<QVariant>() << rect.x() << rect.y() << rect.width() << rect.height();
}
-QRect AtSpiAdaptor::translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect)
+bool AtSpiAdaptor::isValidCoordType(uint coordType)
{
- QAccessibleInterface * window = getWindow(interface);
- if (window)
- return rect.translated(-window->rect().x(), -window->rect().y());
+ 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";
+ return false;
+}
+
+QRect AtSpiAdaptor::translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &screenRect, uint targetCoordType)
+{
+ Q_ASSERT(isValidCoordType(targetCoordType));
+
+ QAccessibleInterface *upper = nullptr;
+ if (targetCoordType == ATSPI_COORD_TYPE_WINDOW)
+ upper = getWindow(interface);
+ else if (targetCoordType == ATSPI_COORD_TYPE_PARENT)
+ upper = interface->parent();
+
+ QRect rect = screenRect;
+ if (upper)
+ rect.translate(-upper->rect().x(), -upper->rect().y());
return rect;
}
+QPoint AtSpiAdaptor::translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType)
+{
+ Q_ASSERT(isValidCoordType(fromCoordType));
+
+ QAccessibleInterface *upper = nullptr;
+ if (fromCoordType == ATSPI_COORD_TYPE_WINDOW)
+ upper = getWindow(interface);
+ else if (fromCoordType == ATSPI_COORD_TYPE_PARENT)
+ upper = interface->parent();
+
+ QPoint screenPos = pos;
+ if (upper)
+ screenPos += upper->rect().topLeft();
+
+ return screenPos;
+}
// Editable Text interface
static QString textForRange(QAccessibleInterface *accessible, int startOffset, int endOffset)
@@ -2100,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);
}
@@ -2108,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
@@ -2173,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;
@@ -2195,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)
@@ -2207,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
@@ -2222,24 +2510,79 @@ 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;
}
- if (0) {
- // properties
- } else if (function == "GetCaption"_L1) {
+ if (function == "GetCaption"_L1) {
QAccessibleInterface * captionInterface= interface->tableInterface()->caption();
if (captionInterface) {
QSpiObjectReference ref = QSpiObjectReference(connection, QDBusObjectPath(pathForInterface(captionInterface)));
- sendReply(connection, message, QVariant::fromValue(ref));
+ sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(ref))));
} else {
- sendReply(connection, message, QVariant::fromValue(
- QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))));
+ sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(
+ QSpiObjectReference(connection, QDBusObjectPath(ATSPI_DBUS_PATH_NULL))))));
}
} else if (function == "GetNColumns"_L1) {
connection.send(message.createReply(QVariant::fromValue(QDBusVariant(
@@ -2264,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;
}
@@ -2273,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)));
@@ -2283,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);
@@ -2303,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();
@@ -2315,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;
}
}
@@ -2351,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;
@@ -2368,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();
@@ -2391,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))));
}
@@ -2413,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)));
@@ -2428,9 +2786,67 @@ 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;
+}
+
+// Table cell interface
+bool AtSpiAdaptor::tableCellInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection)
+{
+ QAccessibleTableCellInterface* cellInterface = interface->tableCellInterface();
+ if (!cellInterface) {
+ qCWarning(lcAccessibilityAtspi) << "Could not find table cell interface for: " << message.path() << interface;
+ return false;
+ }
+
+ 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) {
+ const int row = cellInterface->rowIndex();
+ 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())))));
+ } else if (function == "GetRowColumnSpan"_L1) {
+ QVariantList list;
+ list << cellInterface->rowIndex() << cellInterface->columnIndex() << cellInterface->rowExtent() << cellInterface->columnExtent();
+ connection.send(message.createReply(list));
+ } else if (function == "GetTable"_L1) {
+ QSpiObjectReference ref;
+ QAccessibleInterface* table = cellInterface->table();
+ 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;
}
diff --git a/src/gui/accessible/linux/atspiadaptor_p.h b/src/gui/accessible/linux/atspiadaptor_p.h
index 3d785e4c25..3a890f3d7d 100644
--- a/src/gui/accessible/linux/atspiadaptor_p.h
+++ b/src/gui/accessible/linux/atspiadaptor_p.h
@@ -19,7 +19,6 @@
#include <atspi/atspi-constants.h>
#include <QtGui/private/qtguiglobal_p.h>
-#include <QtCore/qsharedpointer.h>
#include <QtDBus/qdbusvirtualobject.h>
#include <QtGui/qaccessible.h>
@@ -31,7 +30,6 @@ QT_REQUIRE_CONFIG(accessibility);
QT_BEGIN_NAMESPACE
class QAccessibleInterface;
-class QSpiAccessibleInterface;
class QSpiApplicationAdaptor;
@@ -48,8 +46,6 @@ public:
bool handleMessage(const QDBusMessage &message, const QDBusConnection &connection) override;
void notify(QAccessibleEvent *event);
- void init();
- void checkInitializedAndEnabled();
public Q_SLOTS:
void eventListenerRegistered(const QString &bus, const QString &path);
void eventListenerDeregistered(const QString &bus, const QString &path);
@@ -77,7 +73,9 @@ private:
bool textInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool editableTextInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool valueInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+ bool selectionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
bool tableInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
+ bool tableCellInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection);
void sendReply(const QDBusConnection &connection, const QDBusMessage &message, const QVariant &argument) const;
@@ -94,17 +92,21 @@ private:
// component helper functions
static QRect getExtents(QAccessibleInterface *interface, uint coordType);
- static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect);
+ static bool isValidCoordType(uint coordType);
+ static QRect translateFromScreenCoordinates(QAccessibleInterface *interface, const QRect &rect, uint targetCoordType);
+ static QPoint translateToScreenCoordinates(QAccessibleInterface *interface, const QPoint &pos, uint fromCoordType);
// action helper functions
QSpiActionArray getActions(QAccessibleInterface *interface) const;
// text helper functions
QVariantList getAttributes(QAccessibleInterface *, int offset, bool includeDefaults) const;
- QVariantList getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const;
+ QString getAttributeValue(QAccessibleInterface *, int offset, const QString &attributeName) const;
QList<QVariant> getCharacterExtents(QAccessibleInterface *, int offset, uint coordType) const;
QList<QVariant> getRangeExtents(QAccessibleInterface *, int startOffset, int endOffset, uint coordType) const;
- QAccessible::TextBoundaryType qAccessibleBoundaryType(int atspiTextBoundaryType) const;
+ static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiBoundaryType(int atspiTextBoundaryType);
+ static bool isValidAtspiTextGranularity(uint coordType);
+ static QAccessible::TextBoundaryType qAccessibleBoundaryTypeFromAtspiTextGranularity(uint atspiTextGranularity);
static bool inheritsQAction(QObject *object);
// private vars
diff --git a/src/gui/accessible/linux/dbusconnection.cpp b/src/gui/accessible/linux/dbusconnection.cpp
index b4a8643474..10bd10927e 100644
--- a/src/gui/accessible/linux/dbusconnection.cpp
+++ b/src/gui/accessible/linux/dbusconnection.cpp
@@ -56,11 +56,13 @@ DBusConnection::DBusConnection(QObject *parent)
if (c.interface()->isServiceRegistered(A11Y_SERVICE))
serviceRegistered();
- // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work
- QString address = getAddressFromXCB();
- if (!address.isEmpty()) {
- m_enabled = true;
- connectA11yBus(address);
+ if (QGuiApplication::platformName().startsWith("xcb"_L1)) {
+ // In addition try if there is an xatom exposing the bus address, this allows applications run as root to work
+ QString address = getAddressFromXCB();
+ if (!address.isEmpty()) {
+ m_enabled = true;
+ connectA11yBus(address);
+ }
}
}
diff --git a/src/gui/accessible/linux/dbusxml/Socket.xml b/src/gui/accessible/linux/dbusxml/Socket.xml
index 75ec99f994..f9ac76d2c8 100644
--- a/src/gui/accessible/linux/dbusxml/Socket.xml
+++ b/src/gui/accessible/linux/dbusxml/Socket.xml
@@ -17,7 +17,7 @@
<signal name="Available">
<arg direction="in" name="socket" type="(so)"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiObjectReference"/>
- </method>
+ </signal>
</interface>
</node>
diff --git a/src/gui/accessible/linux/qspi_constant_mappings.cpp b/src/gui/accessible/linux/qspi_constant_mappings.cpp
index b3e8816df5..e5b6e3f634 100644
--- a/src/gui/accessible/linux/qspi_constant_mappings.cpp
+++ b/src/gui/accessible/linux/qspi_constant_mappings.cpp
@@ -36,6 +36,8 @@ quint64 spiStatesFromQState(QAccessible::State state)
setSpiStateBit(&spiState, ATSPI_STATE_FOCUSED);
if (state.pressed)
setSpiStateBit(&spiState, ATSPI_STATE_PRESSED);
+ if (state.checkable)
+ setSpiStateBit(&spiState, ATSPI_STATE_CHECKABLE);
if (state.checked)
setSpiStateBit(&spiState, ATSPI_STATE_CHECKED);
if (state.checkStateMixed)
@@ -75,7 +77,8 @@ quint64 spiStatesFromQState(QAccessible::State state)
if (state.extSelectable)
setSpiStateBit(&spiState, ATSPI_STATE_SELECTABLE);
// if (state.Protected)
- // if (state.HasPopup)
+ if (state.hasPopup)
+ setSpiStateBit(&spiState, ATSPI_STATE_HAS_POPUP);
if (state.modal)
setSpiStateBit(&spiState, ATSPI_STATE_MODAL);
if (state.multiLine)
@@ -97,6 +100,7 @@ QSpiUIntList spiStateSetFromSpiStates(quint64 states)
AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relation)
{
+ // direction of the relation is "inversed" in Qt and AT-SPI
switch (relation) {
case QAccessible::Label:
return ATSPI_RELATION_LABELLED_BY;
@@ -106,6 +110,14 @@ AtspiRelationType qAccessibleRelationToAtSpiRelation(QAccessible::Relation relat
return ATSPI_RELATION_CONTROLLED_BY;
case QAccessible::Controlled:
return ATSPI_RELATION_CONTROLLER_FOR;
+ case QAccessible::DescriptionFor:
+ return ATSPI_RELATION_DESCRIBED_BY;
+ case QAccessible::Described:
+ return ATSPI_RELATION_DESCRIPTION_FOR;
+ case QAccessible::FlowsFrom:
+ return ATSPI_RELATION_FLOWS_TO;
+ case QAccessible::FlowsTo:
+ return ATSPI_RELATION_FLOWS_FROM;
default:
qWarning() << "Cannot return AT-SPI relation for:" << relation;
}
diff --git a/src/gui/accessible/linux/qspiaccessiblebridge.cpp b/src/gui/accessible/linux/qspiaccessiblebridge.cpp
index 8961055f1b..de2e7d5fc0 100644
--- a/src/gui/accessible/linux/qspiaccessiblebridge.cpp
+++ b/src/gui/accessible/linux/qspiaccessiblebridge.cpp
@@ -33,6 +33,14 @@ QSpiAccessibleBridge::QSpiAccessibleBridge()
{
dbusConnection = new DBusConnection();
connect(dbusConnection, SIGNAL(enabledChanged(bool)), this, SLOT(enabledChanged(bool)));
+ // Now that we have connected the signal, make sure we didn't miss a change,
+ // e.g. when running as root or when AT_SPI_BUS_ADDRESS is set by hand.
+ // But do that only on next loop, once dbus is really settled.
+ QTimer::singleShot(
+ 0, this, [this]{
+ if (dbusConnection->isEnabled() && dbusConnection->connection().isConnected())
+ enabledChanged(true);
+ });
}
void QSpiAccessibleBridge::enabledChanged(bool enabled)
@@ -197,7 +205,11 @@ static RoleMapping map[] = {
//: Role of an accessible object
{ QAccessible::ButtonDropDown, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down") },
//: Role of an accessible object
+#if ATSPI_ROLE_COUNT > 130
+ { QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON_MENU, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") },
+#else
{ QAccessible::ButtonMenu, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button menu") },
+#endif
//: Role of an accessible object - a button that expands a grid.
{ QAccessible::ButtonDropGrid, ATSPI_ROLE_PUSH_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "button with drop down grid") },
//: Role of an accessible object - blank space between other objects.
diff --git a/src/gui/accessible/linux/qspiapplicationadaptor.cpp b/src/gui/accessible/linux/qspiapplicationadaptor.cpp
index 1cac4598ca..37d7648984 100644
--- a/src/gui/accessible/linux/qspiapplicationadaptor.cpp
+++ b/src/gui/accessible/linux/qspiapplicationadaptor.cpp
@@ -12,12 +12,16 @@
#include "deviceeventcontroller_adaptor.h"
#include "atspi/atspi-constants.h"
+#if __has_include(<xcb/xproto.h>)
#include <xcb/xproto.h>
+#endif
//#define KEYBOARD_DEBUG
QT_BEGIN_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
+
/*!
\class QSpiApplicationAdaptor
\internal
@@ -125,9 +129,13 @@ bool QSpiApplicationAdaptor::eventFilter(QObject *target, QEvent *event)
de.modifiers = 0;
if ((keyEvent->modifiers() & Qt::ShiftModifier) && (keyEvent->key() != Qt::Key_Shift))
de.modifiers |= 1 << ATSPI_MODIFIER_SHIFT;
- // TODO rather introduce Qt::CapslockModifier into KeyboardModifier
- if (keyEvent->nativeModifiers() & XCB_MOD_MASK_LOCK )
- de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK;
+#ifdef XCB_MOD_MASK_LOCK
+ if (QGuiApplication::platformName().startsWith("xcb"_L1)) {
+ // TODO rather introduce Qt::CapslockModifier into KeyboardModifier
+ if (keyEvent->nativeModifiers() & XCB_MOD_MASK_LOCK )
+ de.modifiers |= 1 << ATSPI_MODIFIER_SHIFTLOCK;
+ }
+#endif
if ((keyEvent->modifiers() & Qt::ControlModifier) && (keyEvent->key() != Qt::Key_Control))
de.modifiers |= 1 << ATSPI_MODIFIER_CONTROL;
if ((keyEvent->modifiers() & Qt::AltModifier) && (keyEvent->key() != Qt::Key_Alt))
@@ -173,11 +181,11 @@ QKeyEvent* QSpiApplicationAdaptor::copyKeyEvent(QKeyEvent* old)
void QSpiApplicationAdaptor::notifyKeyboardListenerCallback(const QDBusMessage& message)
{
- if (!keyEvents.length()) {
+ if (!keyEvents.size()) {
qWarning("QSpiApplication::notifyKeyboardListenerCallback with no queued key called");
return;
}
- Q_ASSERT(message.arguments().length() == 1);
+ Q_ASSERT(message.arguments().size() == 1);
if (message.arguments().at(0).toBool() == true) {
QPair<QPointer<QObject>, QKeyEvent*> event = keyEvents.dequeue();
delete event.second;