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"