Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qspimatchrulematcher.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#if QT_CONFIG(accessibility)
7
8#include "atspiadaptor_p.h"
9#include "qspi_constant_mappings_p.h"
10
11QT_BEGIN_NAMESPACE
12
13using namespace Qt::StringLiterals;
14
15Q_STATIC_LOGGING_CATEGORY(lcAccessibilityAtspi, "qt.accessibility.atspi")
16
17QSpiMatchRuleMatcher::QSpiMatchRuleMatcher(const QSpiMatchRule &matchRule)
18 : m_states(spiStatesFromSpiStateSet(matchRule.states)),
19 m_stateMatchType(matchRule.stateMatchType),
20 m_attributes(matchRule.attributes),
21 m_attributeMatchType(matchRule.attributeMatchType),
22 m_roleMatchType(matchRule.roleMatchType),
23 m_interfaceMatchType(matchRule.interfaceMatchType)
24{
25 // extract roles encoded in bitset stored in multiple 32 bit integers
26 for (qsizetype i = 0; i < matchRule.roles.size(); ++i) {
27 const auto role = matchRule.roles.at(i);
28 for (int j = 0; j < 32; j++) {
29 if (role & (1 << j)) {
30 const auto atspiRole = i * 32 + j;
31 if (atspiRole < ATSPI_ROLE_LAST_DEFINED)
32 m_roles.insert(AtspiRole(atspiRole));
33 else
34 qCWarning(lcAccessibilityAtspi)
35 << "Ignoring invalid AT-SPI role value" << atspiRole;
36 }
37 }
38 }
39
40 // use qualified interface names to match what AtSpiAdaptor::accessibleInterfaces returns
41 m_interfaces.reserve(matchRule.interfaces.size());
42 for (const QString &ifaceName : matchRule.interfaces)
43 m_interfaces.push_back("org.a11y.atspi."_L1 + ifaceName);
44}
45
46bool QSpiMatchRuleMatcher::matchAttributes(QAccessibleInterface &iface) const
47{
48 switch (m_attributeMatchType) {
49 case ATSPI_Collection_MATCH_EMPTY:
50 if (m_attributes.empty())
51 return AtSpiAdaptor::getAttributes(&iface).isEmpty();
52 [[fallthrough]];
53 case ATSPI_Collection_MATCH_ALL: {
54 if (m_attributes.empty())
55 return true;
56 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
57 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
58 if (!attributes.contains(key) || attributes[key] != value)
59 return false;
60 }
61 return true;
62 }
63 case ATSPI_Collection_MATCH_ANY: {
64 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
65 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
66 if (attributes.contains(key) && attributes[key] == value)
67 return true;
68 }
69 return false;
70 }
71 case ATSPI_Collection_MATCH_NONE: {
72 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
73 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
74 if (attributes.contains(key) && attributes[key] == value)
75 return false;
76 }
77 return true;
78 }
79 default:
80 qCWarning(lcAccessibilityAtspi)
81 << "QSpiMatchRuleMatcher::matchAttributes called with invalid match type "
82 << m_attributeMatchType;
83 return false;
84 }
85}
86
87bool QSpiMatchRuleMatcher::matchInterfaces(QAccessibleInterface &iface) const
88{
89 switch (m_interfaceMatchType) {
90 case ATSPI_Collection_MATCH_EMPTY:
91 if (m_interfaces.empty())
92 return AtSpiAdaptor::accessibleInterfaces(&iface).isEmpty();
93 [[fallthrough]];
94 case ATSPI_Collection_MATCH_ALL: {
95 if (m_interfaces.empty())
96 return true;
97 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
98 for (const QString &atSpiInterface : m_interfaces) {
99 if (!interfaces.contains(atSpiInterface))
100 return false;
101 }
102 return true;
103 }
104 case ATSPI_Collection_MATCH_ANY: {
105 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
106 for (const QString &atSpiInterface : m_interfaces) {
107 if (interfaces.contains(atSpiInterface))
108 return true;
109 }
110 return false;
111 }
112 case ATSPI_Collection_MATCH_NONE: {
113 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
114 for (const QString &atSpiInterface : m_interfaces) {
115 if (interfaces.contains(atSpiInterface))
116 return false;
117 }
118 return true;
119 }
120 default:
121 qCWarning(lcAccessibilityAtspi)
122 << "QSpiMatchRuleMatcher::matchInterfaces called with invalid match type "
123 << m_interfaceMatchType;
124 return false;
125 }
126}
127
128bool QSpiMatchRuleMatcher::matchRoles(QAccessibleInterface &iface) const
129{
130 switch (m_roleMatchType) {
131 case ATSPI_Collection_MATCH_EMPTY:
132 if (m_roles.empty())
133 // accessible always has exactly one role, i.e. can't have no roles
134 return false;
135 [[fallthrough]];
136 case ATSPI_Collection_MATCH_ALL:
137 if (m_roles.empty())
138 return true;
139 if (m_roles.size() > 1)
140 // accessible only has a single role
141 return false;
142 [[fallthrough]];
143 case ATSPI_Collection_MATCH_ANY:
144 return m_roles.find(AtSpiAdaptor::getRole(&iface)) != m_roles.end();
145 case ATSPI_Collection_MATCH_NONE:
146 return m_roles.find(AtSpiAdaptor::getRole(&iface)) == m_roles.end();
147 default:
148 qCWarning(lcAccessibilityAtspi)
149 << "QSpiMatchRuleMatcher::matchRoles called with invalid match type "
150 << m_roleMatchType;
151 return false;
152 }
153}
154
155bool QSpiMatchRuleMatcher::matchStates(QAccessibleInterface &iface) const
156{
157 switch (m_stateMatchType) {
158 case ATSPI_Collection_MATCH_EMPTY:
159 if (m_states == 0)
160 return spiStatesFromQState(iface.state()) == 0;
161 [[fallthrough]];
162 case ATSPI_Collection_MATCH_ALL:
163 return (spiStatesFromQState(iface.state()) & m_states) == m_states;
164 case ATSPI_Collection_MATCH_ANY:
165 return (spiStatesFromQState(iface.state()) & m_states) != 0;
166 case ATSPI_Collection_MATCH_NONE:
167 return (spiStatesFromQState(iface.state()) & m_states) == 0;
168 default:
169 qCWarning(lcAccessibilityAtspi)
170 << "QSpiMatchRuleMatcher::matchStates called with invalid match type "
171 << m_stateMatchType;
172 return false;
173 }
174}
175
176bool QSpiMatchRuleMatcher::match(QAccessibleInterface &iface) const
177{
178 return matchRoles(iface) && matchStates(iface) && matchInterfaces(iface)
179 && matchAttributes(iface);
180}
181
182QT_END_NAMESPACE
183
184#endif // QT_CONFIG(accessibility)