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