LCOV - code coverage report
Current view: top level - muse/framework/diagnostics/qml/Muse/Diagnostics - diagnosticaccessiblemodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 197 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "diagnosticaccessiblemodel.h"
      23              : 
      24              : #include <QAccessible>
      25              : #include <QMetaEnum>
      26              : 
      27              : #include "muse_framework_config.h"
      28              : #include "accessibility/internal/accessibilitycontroller.h"
      29              : 
      30              : #include "log.h"
      31              : 
      32              : using namespace muse::diagnostics;
      33              : using namespace muse::accessibility;
      34              : 
      35            0 : DiagnosticAccessibleModel::DiagnosticAccessibleModel(QObject* parent)
      36            0 :     : QAbstractItemModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
      37              : {
      38            0 :     m_refresher.setInterval(1000);
      39            0 :     m_refresher.setSingleShot(false);
      40            0 :     connect(&m_refresher, &QTimer::timeout, [this]() {
      41            0 :         reload();
      42            0 :     });
      43            0 : }
      44              : 
      45            0 : DiagnosticAccessibleModel::~DiagnosticAccessibleModel()
      46              : {
      47            0 :     delete m_rootItem;
      48            0 : }
      49              : 
      50            0 : void DiagnosticAccessibleModel::init()
      51              : {
      52              : #ifdef MUSE_MODULE_ACCESSIBILITY
      53            0 :     AccessibilityController* accessController = dynamic_cast<AccessibilityController*>(accessibilityController().get());
      54            0 :     AccessibilityController::Item rootItem = accessController->findItem(accessController);
      55            0 :     m_accessibleRootObject = rootItem.object;
      56              : 
      57            0 :     accessController->eventSent().onReceive(this, [this](QAccessibleEvent* ev) { onAccessibleEvent(ev); });
      58              : #endif
      59            0 : }
      60              : 
      61              : #ifdef MUSE_MODULE_ACCESSIBILITY
      62            0 : static void debug_dumpItem(QAccessibleInterface* item, QTextStream& stream, QString& level)
      63              : {
      64            0 :     QAccessibleInterface* prn = item->parent();
      65            0 :     stream << level
      66            0 :            << item->text(QAccessible::Name)
      67            0 :            << " parent: " << (prn ? prn->text(QAccessible::Name) : QString("null"))
      68            0 :            << " childCount: " << item->childCount()
      69            0 :            << Qt::endl;
      70              : 
      71            0 :     QAccessible::State st = item->state();
      72            0 :     stream << level
      73            0 :            << "state:"
      74            0 :            << " invisible: " << st.invisible
      75            0 :            << " invalid: " << st.invalid
      76            0 :            << " disabled: " << st.disabled
      77            0 :            << " active: " << st.active
      78            0 :            << " focusable: " << st.focusable
      79            0 :            << " focused: " << st.focused
      80            0 :            << Qt::endl;
      81              : 
      82            0 :     level += "  ";
      83            0 :     int count = item->childCount();
      84            0 :     for (int i = 0; i < count; ++i) {
      85            0 :         QAccessibleInterface* ch = item->child(i);
      86            0 :         debug_dumpItem(ch, stream, level);
      87              :     }
      88            0 :     level.chop(2);
      89            0 : }
      90              : 
      91            0 : static void debug_dumpTree(QAccessibleInterface* item)
      92              : {
      93            0 :     QString str;
      94            0 :     QTextStream stream(&str);
      95            0 :     QString level;
      96              : 
      97            0 :     debug_dumpItem(item, stream, level);
      98              : 
      99            0 :     stream.flush();
     100            0 :     LOGI() << "================================================\n";
     101            0 :     std::cout << str.toStdString() << '\n';
     102            0 : }
     103              : 
     104              : #endif
     105              : 
     106            0 : void DiagnosticAccessibleModel::dumpTree()
     107              : {
     108              : #ifdef MUSE_MODULE_ACCESSIBILITY
     109            0 :     AccessibilityController* accessController = dynamic_cast<AccessibilityController*>(accessibilityController().get());
     110            0 :     AccessibilityController::Item rootItem = accessController->findItem(accessController);
     111              : 
     112            0 :     debug_dumpTree(rootItem.iface);
     113              : #endif
     114            0 : }
     115              : 
     116            0 : QModelIndex DiagnosticAccessibleModel::index(int row, int column, const QModelIndex& parent) const
     117              : {
     118            0 :     if (!m_rootItem) {
     119            0 :         return QModelIndex();
     120              :     }
     121              : 
     122            0 :     if (!hasIndex(row, column, parent)) {
     123            0 :         return QModelIndex();
     124              :     }
     125              : 
     126            0 :     Item* parentItem = nullptr;
     127              : 
     128            0 :     if (!parent.isValid()) {
     129            0 :         parentItem = m_rootItem;
     130              :     } else {
     131            0 :         parentItem = itemByModelIndex(parent);
     132              :     }
     133              : 
     134            0 :     if (!parentItem) {
     135            0 :         return QModelIndex();
     136              :     }
     137              : 
     138            0 :     Item* childItem = parentItem->child(row);
     139            0 :     if (childItem) {
     140            0 :         return createIndex(row, column, childItem->key());
     141              :     }
     142              : 
     143            0 :     return QModelIndex();
     144              : }
     145              : 
     146            0 : QModelIndex DiagnosticAccessibleModel::parent(const QModelIndex& child) const
     147              : {
     148            0 :     if (!m_rootItem) {
     149            0 :         return QModelIndex();
     150              :     }
     151              : 
     152            0 :     if (!child.isValid()) {
     153            0 :         return QModelIndex();
     154              :     }
     155              : 
     156            0 :     Item* childItem = itemByModelIndex(child);
     157            0 :     if (!childItem) {
     158            0 :         return QModelIndex();
     159              :     }
     160              : 
     161            0 :     Item* parentItem = childItem->parent();
     162            0 :     if (parentItem == m_rootItem) {
     163            0 :         return QModelIndex();
     164              :     }
     165              : 
     166            0 :     return createIndex(parentItem->row(), 0, parentItem->key());
     167              : }
     168              : 
     169            0 : int DiagnosticAccessibleModel::rowCount(const QModelIndex& parent) const
     170              : {
     171            0 :     if (!m_rootItem) {
     172            0 :         return 0;
     173              :     }
     174              : 
     175            0 :     Item* parentItem = nullptr;
     176              : 
     177            0 :     if (!parent.isValid()) {
     178            0 :         parentItem = m_rootItem;
     179              :     } else {
     180            0 :         parentItem = itemByModelIndex(parent);
     181              :     }
     182              : 
     183            0 :     if (!parentItem) {
     184            0 :         return 0;
     185              :     }
     186              : 
     187            0 :     return parentItem->childCount();
     188              : }
     189              : 
     190            0 : int DiagnosticAccessibleModel::columnCount(const QModelIndex&) const
     191              : {
     192            0 :     return 1;
     193              : }
     194              : 
     195            0 : QVariant DiagnosticAccessibleModel::data(const QModelIndex& index, int role) const
     196              : {
     197            0 :     if (!index.isValid() && role != rItemData) {
     198            0 :         return QVariant();
     199              :     }
     200              : 
     201            0 :     Item* item = itemByModelIndex(index);
     202            0 :     if (!item) {
     203            0 :         return QVariant();
     204              :     }
     205              : 
     206            0 :     return item->data();
     207              : }
     208              : 
     209            0 : QHash<int, QByteArray> DiagnosticAccessibleModel::roleNames() const
     210              : {
     211            0 :     return { { rItemData, "itemData" } };
     212            0 : }
     213              : 
     214            0 : void DiagnosticAccessibleModel::onAccessibleEvent(QAccessibleEvent* ev)
     215              : {
     216            0 :     LOGD() << "event: " << ev->type();
     217            0 :     if (ev->type() == QAccessible::ObjectCreated || ev->type() == QAccessible::ObjectDestroyed) {
     218              :         // todo
     219              :     } else {
     220            0 :         onItemChanged(ev->object());
     221              :     }
     222            0 : }
     223              : 
     224            0 : void DiagnosticAccessibleModel::onItemChanged(QObject* accessibleObject)
     225              : {
     226            0 :     if (!m_rootItem) {
     227            0 :         return;
     228              :     }
     229              : 
     230            0 :     QAccessibleInterface* iface = QAccessible::queryAccessibleInterface(accessibleObject);
     231            0 :     IF_ASSERT_FAILED(iface) {
     232            0 :         return;
     233              :     }
     234              : 
     235            0 :     Item* item = findItemForIface(iface, m_rootItem);
     236            0 :     if (!item) {
     237            0 :         LOGE() << "not found item: " << iface->text(QAccessible::Name);
     238            0 :         return;
     239              :     }
     240              : 
     241            0 :     QVariant newData = makeData(iface);
     242            0 :     item->setData(newData);
     243              : 
     244            0 :     QModelIndex index = createIndex(item->row(), 0, item->key());
     245              : 
     246            0 :     QAccessible::State st = iface->state();
     247            0 :     if (st.focused) {
     248            0 :         emit focusedItem(index);
     249              :     }
     250              : 
     251            0 :     emit dataChanged(index, index, { rItemData });
     252            0 : }
     253              : 
     254            0 : DiagnosticAccessibleModel::Item* DiagnosticAccessibleModel::findItemForIface(const QAccessibleInterface* iface, Item* rootItem) const
     255              : {
     256            0 :     if (!rootItem) {
     257            0 :         return nullptr;
     258              :     }
     259              : 
     260            0 :     if (rootItem->iface() == iface) {
     261            0 :         return rootItem;
     262              :     }
     263              : 
     264            0 :     for (int i = 0; i < rootItem->childCount(); ++i) {
     265            0 :         Item* it = findItemForIface(iface, rootItem->child(i));
     266            0 :         if (it) {
     267            0 :             return it;
     268              :         }
     269              :     }
     270              : 
     271            0 :     return nullptr;
     272              : }
     273              : 
     274            0 : DiagnosticAccessibleModel::Item* DiagnosticAccessibleModel::createItem(Item* parent)
     275              : {
     276            0 :     Item* item = new Item(parent);
     277            0 :     m_allItems.insert(item->key(), item);
     278            0 :     return item;
     279              : }
     280              : 
     281            0 : DiagnosticAccessibleModel::Item* DiagnosticAccessibleModel::itemByModelIndex(const QModelIndex& index) const
     282              : {
     283            0 :     return m_allItems.value(index.internalId(), nullptr);
     284              : }
     285              : 
     286            0 : void DiagnosticAccessibleModel::reload()
     287              : {
     288            0 :     IF_ASSERT_FAILED(m_accessibleRootObject) {
     289            0 :         return;
     290              :     }
     291              : 
     292            0 :     QAccessibleInterface* iface = QAccessible::queryAccessibleInterface(m_accessibleRootObject);
     293            0 :     IF_ASSERT_FAILED(iface) {
     294            0 :         return;
     295              :     }
     296              : 
     297            0 :     m_refresher.stop();
     298              : 
     299            0 :     emit beforeReload();
     300            0 :     beginResetModel();
     301              : 
     302            0 :     m_allItems.clear();
     303            0 :     delete m_rootItem;
     304            0 :     m_rootItem = createItem(nullptr);
     305            0 :     load(iface, m_rootItem);
     306              : 
     307            0 :     endResetModel();
     308            0 :     emit afterReload();
     309              : 
     310            0 :     if (m_isAutoRefresh) {
     311            0 :         m_refresher.start();
     312              :     }
     313              : }
     314              : 
     315            0 : QVariant DiagnosticAccessibleModel::makeData(const QAccessibleInterface* iface) const
     316              : {
     317            0 :     QAccessibleInterface* prn = iface->parent();
     318              : 
     319            0 :     static const QMetaEnum roleEnum = QMetaEnum::fromType<QAccessible::Role>();
     320              : 
     321            0 :     QVariantMap itemData;
     322            0 :     itemData["role"] = QString(roleEnum.valueToKey(iface->role()));
     323            0 :     itemData["name"] = iface->text(QAccessible::Name);
     324            0 :     itemData["parent"] = prn ? prn->text(QAccessible::Name) : QString("null");
     325            0 :     itemData["children"] = iface->childCount();
     326              : 
     327            0 :     QAccessible::State st = iface->state();
     328            0 :     QVariantMap state;
     329            0 :     state["invisible"] = st.invisible;
     330            0 :     state["invalid"] = st.invalid;
     331            0 :     state["disabled"] = st.disabled;
     332            0 :     state["active"] = st.active;
     333            0 :     state["focusable"] = st.focusable;
     334            0 :     state["focused"] = st.focused;
     335            0 :     itemData["state"] = state;
     336              : 
     337            0 :     return itemData;
     338            0 : }
     339              : 
     340            0 : void DiagnosticAccessibleModel::load(QAccessibleInterface* iface, Item* parent)
     341              : {
     342            0 :     Item* item = createItem(parent);
     343            0 :     item->setIface(iface);
     344            0 :     item->setData(makeData(iface));
     345              : 
     346            0 :     int childCount = iface->childCount();
     347            0 :     for (int i = 0; i < childCount; ++i) {
     348            0 :         QAccessibleInterface* child = iface->child(i);
     349            0 :         load(child, item);
     350              :     }
     351            0 : }
     352              : 
     353            0 : void DiagnosticAccessibleModel::setIsAutoRefresh(bool isAutoRefresh)
     354              : {
     355            0 :     if (m_isAutoRefresh == isAutoRefresh) {
     356            0 :         return;
     357              :     }
     358              : 
     359            0 :     m_isAutoRefresh = isAutoRefresh;
     360            0 :     emit isAutoRefreshChanged();
     361              : 
     362            0 :     if (isAutoRefresh) {
     363            0 :         m_refresher.start();
     364              :     }
     365              : }
     366              : 
     367            0 : bool DiagnosticAccessibleModel::isAutoRefresh() const
     368              : {
     369            0 :     return m_isAutoRefresh;
     370              : }
        

Generated by: LCOV version 2.0-1