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