1 /*
2 * Copyright 2012 Frederik Gladhorn <gladhorn@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "accessibletree.h"
22
23 #include <qdebug.h>
24 #include <qstack.h>
25
26 using namespace QAccessibleClient;
27
parent()28 AccessibleWrapper* AccessibleWrapper::parent()
29 {
30 return m_parent;
31 }
32
child(int index)33 AccessibleWrapper* AccessibleWrapper::child(int index)
34 {
35 if (m_children.isEmpty()) {
36 QList<AccessibleObject> children = acc.children();
37 foreach (const AccessibleObject &c, children) {
38 m_children.append(new AccessibleWrapper(c, this));
39 }
40 }
41 if (index >= 0 && index < m_children.count()) {
42 return m_children.at(index);
43 }
44 return nullptr;
45 }
46
childCount()47 int AccessibleWrapper::childCount()
48 {
49 if (m_children.isEmpty())
50 return acc.childCount();
51 return m_children.count();
52 }
53
AccessibleTree(QObject * parent)54 AccessibleTree::AccessibleTree(QObject* parent)
55 : QAbstractItemModel(parent), m_registry(nullptr)
56 {
57 }
58
~AccessibleTree()59 AccessibleTree::~AccessibleTree()
60 {
61 }
62
headerData(int section,Qt::Orientation orientation,int role) const63 QVariant AccessibleTree::headerData(int section, Qt::Orientation orientation, int role) const
64 {
65 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
66 if (section == 0) {
67 return "Accessible";
68 } else if (section == 1) {
69 return "Role";
70 }
71 }
72 return QVariant();
73 }
74
columnCount(const QModelIndex & parent) const75 int AccessibleTree::columnCount(const QModelIndex& parent) const
76 {
77 Q_UNUSED(parent);
78 return 2;
79 }
80
data(const QModelIndex & index,int role) const81 QVariant AccessibleTree::data(const QModelIndex& index, int role) const
82 {
83 if (!m_registry || !index.isValid())
84 return QVariant();
85
86 AccessibleObject acc = static_cast<AccessibleWrapper*>(index.internalPointer())->acc;
87
88 switch (role) {
89 case Qt::DisplayRole:
90 if (index.column() == 0) {
91 QString name = acc.name();
92 if (name.isEmpty())
93 name = QString("[%1]").arg(acc.roleName());
94 return name;
95 } else if (index.column() == 1) {
96 return acc.roleName();
97 }
98
99 default:
100 return QVariant();
101 }
102 return QVariant();
103 }
104
index(int row,int column,const QModelIndex & parent) const105 QModelIndex AccessibleTree::index(int row, int column, const QModelIndex& parent) const
106 {
107 if (!m_registry || (column < 0) || (column > 1) || (row < 0))
108 return QModelIndex();
109
110 // qDebug() << "index:" << row << column << parent;
111 if (!parent.isValid()) {
112 if (row < m_apps.count()) {
113 return createIndex(row, column, m_apps.at(row));
114 }
115 } else {
116 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer());
117 if (row < wraper->childCount()) {
118 QModelIndex newIndex = createIndex(row, column, wraper->child(row));
119 if (newIndex.parent() != parent) {
120 qWarning() << "Broken navigation: " << parent << row;
121 emit navigationError(parent);
122 }
123 return newIndex;
124 } else {
125 qWarning() << "Could not access child: " << wraper->acc.name() << wraper->acc.roleName();
126 }
127 }
128
129 return QModelIndex();
130 }
131
parent(const QModelIndex & child) const132 QModelIndex AccessibleTree::parent(const QModelIndex& child) const
133 {
134 // qDebug() << "Parent: " << child;
135 if (child.isValid()) {
136 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(child.internalPointer());
137 AccessibleWrapper *parent = wraper->parent();
138 if (parent) {
139 // if this is a top-level item, it has no parent
140 if (parent->parent()) {
141 return createIndex(parent->acc.indexInParent(), 0, parent);
142 } else {
143 return createIndex(m_apps.indexOf(parent), 0, parent);
144 }
145 }
146 }
147
148 return QModelIndex();
149 }
150
rowCount(const QModelIndex & parent) const151 int AccessibleTree::rowCount(const QModelIndex& parent) const
152 {
153 if (!m_registry || parent.column() > 0)
154 return 0;
155
156 // qDebug() << "row count:" << parent << parent.internalPointer();
157 if (!parent.isValid()) {
158 return m_apps.count();
159 } else {
160 if (!parent.internalPointer())
161 return 0;
162
163 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer());
164 // qDebug() << " row count:" << wraper->acc.name() << wraper->acc.roleName() << wraper->childCount();
165 return wraper->childCount();
166 }
167
168 return 0;
169 }
170
setRegistry(QAccessibleClient::Registry * registry)171 void AccessibleTree::setRegistry(QAccessibleClient::Registry* registry)
172 {
173 m_registry = registry;
174 resetModel();
175 }
176
resetModel()177 void AccessibleTree::resetModel()
178 {
179 beginResetModel();
180 qDeleteAll(m_apps);
181 m_apps.clear();
182 if (m_registry) {
183 QList<AccessibleObject> children = m_registry->applications();
184 foreach (const AccessibleObject &c, children) {
185 m_apps.append(new AccessibleWrapper(c, nullptr));
186 }
187 }
188 endResetModel();
189 }
190
updateTopLevelApps()191 void AccessibleTree::updateTopLevelApps()
192 {
193 QList<AccessibleObject> topLevelApps = m_registry->applications();
194 for (int i = m_apps.count() - 1; i >= 0; --i) {
195 AccessibleObject app = m_apps.at(i)->acc;
196 int indexOfApp = topLevelApps.indexOf(app);
197 if (indexOfApp < 0) {
198 removeAccessible(index(i, 0, QModelIndex()));
199 } else {
200 topLevelApps.takeAt(i);
201 }
202 }
203
204 foreach (const AccessibleObject &newApp, topLevelApps) {
205 addAccessible(newApp);
206 }
207 }
208
indexForAccessible(const AccessibleObject & object)209 QModelIndex AccessibleTree::indexForAccessible(const AccessibleObject& object)
210 {
211 if (!object.isValid())
212 return QModelIndex();
213
214 if (object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) {
215 // top level
216 for (int i = 0; i < m_apps.size(); ++i) {
217 if (m_apps.at(i)->acc == object)
218 return createIndex(i, 0, m_apps.at(i));
219 }
220 int lastIndex = m_apps.size();
221 if (addAccessible(object) && m_apps.at(lastIndex)->acc == object)
222 return createIndex(lastIndex, 0, m_apps.at(lastIndex));
223
224 } else {
225 AccessibleObject parent = object.parent();
226 if (parent.isValid()) {
227 QModelIndex parentIndex = indexForAccessible(parent);
228 if (!parentIndex.isValid()) {
229 if (object.isValid() && object.application().isValid())
230 qWarning() << Q_FUNC_INFO << object.application().name() << object.name() << object.roleName() << "Parent model index is invalid: " << object;
231
232 return QModelIndex();
233 }
234 int indexInParent = object.indexInParent();
235 if (indexInParent < 0) {
236 qWarning() << Q_FUNC_INFO << "indexInParent is invalid: " << object;
237 return QModelIndex();
238 }
239 QModelIndex in = index(indexInParent, 0, parentIndex);
240 //qDebug() << "indexForAccessible: " << object.name() << data(in).toString() << " parent: " << data(parentIndex).toString();//" row: " << object.indexInParent() << "parentIndex: " << parentIndex;
241 return in;
242 } else {
243 qWarning() << Q_FUNC_INFO << "Invalid indexForAccessible: " << object;
244 //Q_ASSERT(!object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::Application));
245 //return indexForAccessible(object.application());
246
247 Q_FOREACH(const QAccessibleClient::AccessibleObject &child, object.children()) {
248 if (child.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) {
249 for (int i = 0; i < m_apps.size(); ++i) {
250 if (m_apps.at(i)->acc == object)
251 return createIndex(i, 0, m_apps.at(i));
252 }
253 }
254 }
255 }
256 }
257 return QModelIndex();
258 }
259
addAccessible(const QAccessibleClient::AccessibleObject & object)260 bool AccessibleTree::addAccessible(const QAccessibleClient::AccessibleObject &object)
261 {
262 // qDebug() << Q_FUNC_INFO << object;
263 QAccessibleClient::AccessibleObject parent = object.parent();
264
265 // We have no parent -> top level.
266 if (!parent.isValid()) {
267 if (!object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface))
268 qWarning() << Q_FUNC_INFO << "Found top level accessible that does not implement the application interface" << object;
269
270 beginInsertRows(QModelIndex(), m_apps.count(), m_apps.count());
271 m_apps.append(new AccessibleWrapper(object, nullptr));
272 endInsertRows();
273 return true;
274 }
275
276 // If the parent is not known, add it too.
277 QModelIndex parentIndex = indexForAccessible(parent);
278 if (!parentIndex.isValid()) {
279 if (!addAccessible(parent)) {
280 qWarning() << Q_FUNC_INFO << "Could not add accessible (invalid parent): " << object;
281 return false;
282 }
283 parentIndex = indexForAccessible(parent);
284 Q_ASSERT(parentIndex.isValid());
285 }
286
287 // Add this item (or emit dataChanged, if it's there already).
288 int idx = object.indexInParent();
289 if (idx < 0) {
290 qWarning() << Q_FUNC_INFO << "Could not add accessible (invalid index in parent): " << object;
291 return false;
292 }
293 QModelIndex objectIndex = index(idx, 0, parentIndex);
294 if (objectIndex.isValid() && static_cast<AccessibleWrapper*>(objectIndex.internalPointer())->acc == object) {
295 emit dataChanged(objectIndex, objectIndex);
296 return false;
297 }
298
299 beginInsertRows(parentIndex, idx, idx);
300 AccessibleWrapper *parentWrapper = static_cast<AccessibleWrapper*>(parentIndex.internalPointer());
301 Q_ASSERT(parentWrapper);
302 parentWrapper->m_children.insert(idx, new AccessibleWrapper(object, parentWrapper));
303 endInsertRows();
304 return true;
305 }
306
removeAccessible(const QAccessibleClient::AccessibleObject & object)307 bool AccessibleTree::removeAccessible(const QAccessibleClient::AccessibleObject &object)
308 {
309 qDebug() << Q_FUNC_INFO << object;
310 QModelIndex index = indexForAccessible(object);
311 if (!index.isValid())
312 return false;
313 return removeAccessible(index);
314 }
315
removeAccessible(const QModelIndex & index)316 bool AccessibleTree::removeAccessible(const QModelIndex &index)
317 {
318 qDebug() << Q_FUNC_INFO << index;
319 Q_ASSERT(index.isValid());
320 Q_ASSERT(index.model() == this);
321 QModelIndex parent = index.parent();
322 int row = index.row();
323 bool removed = false;
324 beginRemoveRows(parent, row, row);
325 if (parent.isValid()) {
326 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer());
327 Q_ASSERT(wraper);
328 delete wraper->m_children.takeAt(row);
329 removed = true;
330 } else {
331 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(index.internalPointer());
332 Q_ASSERT(wraper);
333 Q_ASSERT(m_apps[row] == wraper);
334 if (m_apps[row] == wraper) {
335 qDebug() << Q_FUNC_INFO << "Delete application accessible object! indexRow=" << row;
336 delete m_apps.takeAt(row);
337 removed = true;
338 }
339 }
340 endRemoveRows();
341 return removed;
342 }
343
updateAccessible(const QAccessibleClient::AccessibleObject & object)344 void AccessibleTree::updateAccessible(const QAccessibleClient::AccessibleObject &object)
345 {
346 QModelIndex index = indexForAccessible(object);
347 emit dataChanged(index, index);
348 }
349
350