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 std::unordered_set<AtspiRole> atSpiRoles;
27 for (int i = 0; i < matchRule.roles.size(); ++i) {
28 for (int j = 0; j < 32; j++) {
29 if (matchRule.roles.at(i) & (1 << j)) {
30 const int 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 accessibleInterfaces() returns
41 for (const QString &ifaceName : matchRule.interfaces)
42 m_interfaces.push_back("org.a11y.atspi."_L1 + ifaceName);
43}
44
45bool QSpiMatchRuleMatcher::matchAttributes(QAccessibleInterface &iface) const
46{
47 switch (m_attributeMatchType) {
48 case ATSPI_Collection_MATCH_EMPTY:
49 if (m_attributes.empty())
50 return AtSpiAdaptor::getAttributes(&iface).isEmpty();
51 [[fallthrough]];
52 case ATSPI_Collection_MATCH_ALL: {
53 if (m_attributes.empty())
54 return true;
55 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
56 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
57 if (!attributes.contains(key) || attributes[key] != value)
58 return false;
59 }
60 return true;
61 }
62 case ATSPI_Collection_MATCH_ANY: {
63 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
64 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
65 if (attributes.contains(key) && attributes[key] == value)
66 return true;
67 }
68 return false;
69 }
70 case ATSPI_Collection_MATCH_NONE: {
71 const QSpiAttributeSet attributes = AtSpiAdaptor::getAttributes(&iface);
72 for (const auto &[key, value] : m_attributes.asKeyValueRange()) {
73 if (attributes.contains(key) && attributes[key] == value)
74 return false;
75 }
76 return true;
77 }
78 default:
79 qCWarning(lcAccessibilityAtspi)
80 << "QSpiMatchRuleMatcher::matchAttributes called with invalid match type "
81 << m_attributeMatchType;
82 return false;
83 }
84}
85
86bool QSpiMatchRuleMatcher::matchInterfaces(QAccessibleInterface &iface) const
87{
88 switch (m_interfaceMatchType) {
89 case ATSPI_Collection_MATCH_EMPTY:
90 if (m_interfaces.empty())
91 return AtSpiAdaptor::accessibleInterfaces(&iface).isEmpty();
92 [[fallthrough]];
93 case ATSPI_Collection_MATCH_ALL: {
94 if (m_interfaces.empty())
95 return true;
96 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
97 for (const QString &atSpiInterface : m_interfaces) {
98 if (!interfaces.contains(atSpiInterface))
99 return false;
100 }
101 return true;
102 }
103 case ATSPI_Collection_MATCH_ANY: {
104 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
105 for (const QString &atSpiInterface : m_interfaces) {
106 if (interfaces.contains(atSpiInterface))
107 return true;
108 }
109 return false;
110 }
111 case ATSPI_Collection_MATCH_NONE: {
112 const QStringList interfaces = AtSpiAdaptor::accessibleInterfaces(&iface);
113 for (const QString &atSpiInterface : m_interfaces) {
114 if (interfaces.contains(atSpiInterface))
115 return false;
116 }
117 return true;
118 }
119 default:
120 qCWarning(lcAccessibilityAtspi)
121 << "QSpiMatchRuleMatcher::matchInterfaces called with invalid match type "
122 << m_interfaceMatchType;
123 return false;
124 }
125}
126
127bool QSpiMatchRuleMatcher::matchRoles(QAccessibleInterface &iface) const
128{
129 switch (m_roleMatchType) {
130 case ATSPI_Collection_MATCH_EMPTY:
131 if (m_roles.empty())
132 // accessible always has exactly one role, i.e. can't have no roles
133 return false;
134 [[fallthrough]];
135 case ATSPI_Collection_MATCH_ALL:
136 if (m_roles.empty())
137 return true;
138 if (m_roles.size() > 1)
139 // accessible only has a single role
140 return false;
141 [[fallthrough]];
142 case ATSPI_Collection_MATCH_ANY:
143 return m_roles.find(AtSpiAdaptor::getRole(&iface)) != m_roles.end();
144 case ATSPI_Collection_MATCH_NONE:
145 return m_roles.find(AtSpiAdaptor::getRole(&iface)) == m_roles.end();
146 default:
147 qCWarning(lcAccessibilityAtspi)
148 << "QSpiMatchRuleMatcher::matchRoles called with invalid match type "
149 << m_roleMatchType;
150 return false;
151 }
152}
153
154bool QSpiMatchRuleMatcher::matchStates(QAccessibleInterface &iface) const
155{
156 switch (m_stateMatchType) {
157 case ATSPI_Collection_MATCH_EMPTY:
158 if (m_states == 0)
159 return spiStatesFromQState(iface.state()) == 0;
160 [[fallthrough]];
161 case ATSPI_Collection_MATCH_ALL:
162 return (spiStatesFromQState(iface.state()) & m_states) == m_states;
163 case ATSPI_Collection_MATCH_ANY:
164 return (spiStatesFromQState(iface.state()) & m_states) != 0;
165 case ATSPI_Collection_MATCH_NONE:
166 return (spiStatesFromQState(iface.state()) & m_states) == 0;
167 default:
168 qCWarning(lcAccessibilityAtspi)
169 << "QSpiMatchRuleMatcher::matchStates called with invalid match type "
170 << m_stateMatchType;
171 return false;
172 }
173}
174
175bool QSpiMatchRuleMatcher::match(QAccessibleInterface &iface) const
176{
177 return matchRoles(iface) && matchStates(iface) && matchInterfaces(iface)
178 && matchAttributes(iface);
179}
180
181QT_END_NAMESPACE
182
183#endif // QT_CONFIG(accessibility)