Warning, file /libraries/libqaccessibilityclient/examples/accessibleapps/accessibletree.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2012 Frederik Gladhorn <gladhorn@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "accessibletree.h" 0008 0009 #include <QDebug> 0010 #include <QStack> 0011 0012 using namespace QAccessibleClient; 0013 0014 AccessibleWrapper* AccessibleWrapper::parent() 0015 { 0016 return m_parent; 0017 } 0018 0019 AccessibleWrapper* AccessibleWrapper::child(int index) 0020 { 0021 if (m_children.isEmpty()) { 0022 const QList<AccessibleObject> children = acc.children(); 0023 for (const AccessibleObject &c : children) { 0024 m_children.append(new AccessibleWrapper(c, this)); 0025 } 0026 } 0027 if (index >= 0 && index < m_children.count()) { 0028 return m_children.at(index); 0029 } 0030 return nullptr; 0031 } 0032 0033 int AccessibleWrapper::childCount() 0034 { 0035 if (m_children.isEmpty()) 0036 return acc.childCount(); 0037 return m_children.count(); 0038 } 0039 0040 AccessibleTree::AccessibleTree(QObject* parent) 0041 : QAbstractItemModel(parent), m_registry(nullptr) 0042 { 0043 } 0044 0045 AccessibleTree::~AccessibleTree() 0046 { 0047 } 0048 0049 QVariant AccessibleTree::headerData(int section, Qt::Orientation orientation, int role) const 0050 { 0051 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0052 if (section == 0) { 0053 return QStringLiteral("Accessible"); 0054 } else if (section == 1) { 0055 return QStringLiteral("Role"); 0056 } 0057 } 0058 return QVariant(); 0059 } 0060 0061 int AccessibleTree::columnCount(const QModelIndex& parent) const 0062 { 0063 Q_UNUSED(parent); 0064 return 2; 0065 } 0066 0067 QVariant AccessibleTree::data(const QModelIndex& index, int role) const 0068 { 0069 if (!m_registry || !index.isValid()) 0070 return QVariant(); 0071 0072 AccessibleObject acc = static_cast<AccessibleWrapper*>(index.internalPointer())->acc; 0073 0074 switch (role) { 0075 case Qt::DisplayRole: 0076 if (index.column() == 0) { 0077 QString name = acc.name(); 0078 if (name.isEmpty()) 0079 name = QStringLiteral("[%1]").arg(acc.roleName()); 0080 return name; 0081 } else if (index.column() == 1) { 0082 return acc.roleName(); 0083 } 0084 0085 default: 0086 return QVariant(); 0087 } 0088 return QVariant(); 0089 } 0090 0091 QModelIndex AccessibleTree::index(int row, int column, const QModelIndex& parent) const 0092 { 0093 if (!m_registry || (column < 0) || (column > 1) || (row < 0)) 0094 return QModelIndex(); 0095 0096 // qDebug() << "index:" << row << column << parent; 0097 if (!parent.isValid()) { 0098 if (row < m_apps.count()) { 0099 return createIndex(row, column, m_apps.at(row)); 0100 } 0101 } else { 0102 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer()); 0103 if (row < wraper->childCount()) { 0104 QModelIndex newIndex = createIndex(row, column, wraper->child(row)); 0105 if (newIndex.parent() != parent) { 0106 qWarning() << "Broken navigation: " << parent << row; 0107 Q_EMIT navigationError(parent); 0108 } 0109 return newIndex; 0110 } else { 0111 qWarning() << "Could not access child: " << wraper->acc.name() << wraper->acc.roleName(); 0112 } 0113 } 0114 0115 return QModelIndex(); 0116 } 0117 0118 QModelIndex AccessibleTree::parent(const QModelIndex& child) const 0119 { 0120 // qDebug() << "Parent: " << child; 0121 if (child.isValid()) { 0122 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(child.internalPointer()); 0123 AccessibleWrapper *parent = wraper->parent(); 0124 if (parent) { 0125 // if this is a top-level item, it has no parent 0126 if (parent->parent()) { 0127 return createIndex(parent->acc.indexInParent(), 0, parent); 0128 } else { 0129 return createIndex(m_apps.indexOf(parent), 0, parent); 0130 } 0131 } 0132 } 0133 0134 return QModelIndex(); 0135 } 0136 0137 int AccessibleTree::rowCount(const QModelIndex& parent) const 0138 { 0139 if (!m_registry || parent.column() > 0) 0140 return 0; 0141 0142 // qDebug() << "row count:" << parent << parent.internalPointer(); 0143 if (!parent.isValid()) { 0144 return m_apps.count(); 0145 } else { 0146 if (!parent.internalPointer()) 0147 return 0; 0148 0149 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer()); 0150 // qDebug() << " row count:" << wraper->acc.name() << wraper->acc.roleName() << wraper->childCount(); 0151 return wraper->childCount(); 0152 } 0153 0154 return 0; 0155 } 0156 0157 void AccessibleTree::setRegistry(QAccessibleClient::Registry* registry) 0158 { 0159 m_registry = registry; 0160 resetModel(); 0161 } 0162 0163 void AccessibleTree::resetModel() 0164 { 0165 beginResetModel(); 0166 qDeleteAll(m_apps); 0167 m_apps.clear(); 0168 if (m_registry) { 0169 const QList<AccessibleObject> children = m_registry->applications(); 0170 for (const AccessibleObject &c : children) { 0171 m_apps.append(new AccessibleWrapper(c, nullptr)); 0172 } 0173 } 0174 endResetModel(); 0175 } 0176 0177 void AccessibleTree::updateTopLevelApps() 0178 { 0179 QList<AccessibleObject> topLevelApps = m_registry->applications(); 0180 for (int i = m_apps.count() - 1; i >= 0; --i) { 0181 AccessibleObject app = m_apps.at(i)->acc; 0182 int indexOfApp = topLevelApps.indexOf(app); 0183 if (indexOfApp < 0) { 0184 removeAccessible(index(i, 0, QModelIndex())); 0185 } else { 0186 topLevelApps.takeAt(i); 0187 } 0188 } 0189 0190 for (const AccessibleObject &newApp : std::as_const(topLevelApps)) { 0191 addAccessible(newApp); 0192 } 0193 } 0194 0195 QModelIndex AccessibleTree::indexForAccessible(const AccessibleObject& object) 0196 { 0197 if (!object.isValid()) 0198 return QModelIndex(); 0199 0200 if (object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) { 0201 // top level 0202 for (int i = 0; i < m_apps.size(); ++i) { 0203 if (m_apps.at(i)->acc == object) 0204 return createIndex(i, 0, m_apps.at(i)); 0205 } 0206 int lastIndex = m_apps.size(); 0207 if (addAccessible(object) && m_apps.at(lastIndex)->acc == object) 0208 return createIndex(lastIndex, 0, m_apps.at(lastIndex)); 0209 0210 } else { 0211 AccessibleObject parent = object.parent(); 0212 if (parent.isValid()) { 0213 QModelIndex parentIndex = indexForAccessible(parent); 0214 if (!parentIndex.isValid()) { 0215 if (object.isValid() && object.application().isValid()) 0216 qWarning() << Q_FUNC_INFO << object.application().name() << object.name() << object.roleName() << "Parent model index is invalid: " << object; 0217 0218 return QModelIndex(); 0219 } 0220 int indexInParent = object.indexInParent(); 0221 if (indexInParent < 0) { 0222 qWarning() << Q_FUNC_INFO << "indexInParent is invalid: " << object; 0223 return QModelIndex(); 0224 } 0225 QModelIndex in = index(indexInParent, 0, parentIndex); 0226 //qDebug() << "indexForAccessible: " << object.name() << data(in).toString() << " parent: " << data(parentIndex).toString();//" row: " << object.indexInParent() << "parentIndex: " << parentIndex; 0227 return in; 0228 } else { 0229 qWarning() << Q_FUNC_INFO << "Invalid indexForAccessible: " << object; 0230 //Q_ASSERT(!object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::Application)); 0231 //return indexForAccessible(object.application()); 0232 0233 for (const QAccessibleClient::AccessibleObject &child : object.children()) { 0234 if (child.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) { 0235 for (int i = 0; i < m_apps.size(); ++i) { 0236 if (m_apps.at(i)->acc == object) 0237 return createIndex(i, 0, m_apps.at(i)); 0238 } 0239 } 0240 } 0241 } 0242 } 0243 return QModelIndex(); 0244 } 0245 0246 bool AccessibleTree::addAccessible(const QAccessibleClient::AccessibleObject &object) 0247 { 0248 // qDebug() << Q_FUNC_INFO << object; 0249 QAccessibleClient::AccessibleObject parent = object.parent(); 0250 0251 // We have no parent -> top level. 0252 if (!parent.isValid()) { 0253 if (!object.supportedInterfaces().testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) 0254 qWarning() << Q_FUNC_INFO << "Found top level accessible that does not implement the application interface" << object; 0255 0256 beginInsertRows(QModelIndex(), m_apps.count(), m_apps.count()); 0257 m_apps.append(new AccessibleWrapper(object, nullptr)); 0258 endInsertRows(); 0259 return true; 0260 } 0261 0262 // If the parent is not known, add it too. 0263 QModelIndex parentIndex = indexForAccessible(parent); 0264 if (!parentIndex.isValid()) { 0265 if (!addAccessible(parent)) { 0266 qWarning() << Q_FUNC_INFO << "Could not add accessible (invalid parent): " << object; 0267 return false; 0268 } 0269 parentIndex = indexForAccessible(parent); 0270 Q_ASSERT(parentIndex.isValid()); 0271 } 0272 0273 // Add this item (or emit dataChanged, if it's there already). 0274 int idx = object.indexInParent(); 0275 if (idx < 0) { 0276 qWarning() << Q_FUNC_INFO << "Could not add accessible (invalid index in parent): " << object; 0277 return false; 0278 } 0279 QModelIndex objectIndex = index(idx, 0, parentIndex); 0280 if (objectIndex.isValid() && static_cast<AccessibleWrapper*>(objectIndex.internalPointer())->acc == object) { 0281 Q_EMIT dataChanged(objectIndex, objectIndex); 0282 return false; 0283 } 0284 0285 beginInsertRows(parentIndex, idx, idx); 0286 AccessibleWrapper *parentWrapper = static_cast<AccessibleWrapper*>(parentIndex.internalPointer()); 0287 Q_ASSERT(parentWrapper); 0288 parentWrapper->m_children.insert(idx, new AccessibleWrapper(object, parentWrapper)); 0289 endInsertRows(); 0290 return true; 0291 } 0292 0293 bool AccessibleTree::removeAccessible(const QAccessibleClient::AccessibleObject &object) 0294 { 0295 qDebug() << Q_FUNC_INFO << object; 0296 QModelIndex index = indexForAccessible(object); 0297 if (!index.isValid()) 0298 return false; 0299 return removeAccessible(index); 0300 } 0301 0302 bool AccessibleTree::removeAccessible(const QModelIndex &index) 0303 { 0304 qDebug() << Q_FUNC_INFO << index; 0305 Q_ASSERT(index.isValid()); 0306 Q_ASSERT(index.model() == this); 0307 QModelIndex parent = index.parent(); 0308 int row = index.row(); 0309 bool removed = false; 0310 beginRemoveRows(parent, row, row); 0311 if (parent.isValid()) { 0312 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(parent.internalPointer()); 0313 Q_ASSERT(wraper); 0314 delete wraper->m_children.takeAt(row); 0315 removed = true; 0316 } else { 0317 AccessibleWrapper *wraper = static_cast<AccessibleWrapper*>(index.internalPointer()); 0318 Q_ASSERT(wraper); 0319 Q_ASSERT(m_apps[row] == wraper); 0320 if (m_apps[row] == wraper) { 0321 qDebug() << Q_FUNC_INFO << "Delete application accessible object! indexRow=" << row; 0322 delete m_apps.takeAt(row); 0323 removed = true; 0324 } 0325 } 0326 endRemoveRows(); 0327 return removed; 0328 } 0329 0330 void AccessibleTree::updateAccessible(const QAccessibleClient::AccessibleObject &object) 0331 { 0332 QModelIndex index = indexForAccessible(object); 0333 Q_EMIT dataChanged(index, index); 0334 } 0335 0336 #include "moc_accessibletree.cpp"