File indexing completed on 2025-01-26 03:28:30

0001 /*
0002     SPDX-FileCopyrightText: 2012 Sebastian Sauer <sebastian.sauer@kdab.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "accessiblepropertiesmodel.h"
0008 
0009 #include <KLocalizedString>
0010 
0011 #include <qaccessibilityclient/registry.h>
0012 
0013 using namespace QAccessibleClient;
0014 
0015 ObjectPropertiesModel::ObjectPropertiesModel(QObject *parent)
0016     : QStandardItemModel(parent)
0017 {
0018     connect(this, &ObjectPropertiesModel::itemChanged, this, &ObjectPropertiesModel::slotDataChanged);
0019 }
0020 
0021 ObjectPropertiesModel::~ObjectPropertiesModel() = default;
0022 
0023 int ObjectPropertiesModel::columnCount(const QModelIndex &parent) const
0024 {
0025     Q_UNUSED(parent)
0026     constexpr int val = static_cast<int>(ObjectPropertiesModelRoles::LastColumn) + 1;
0027     return val;
0028 }
0029 
0030 void ObjectPropertiesModel::slotDataChanged(QStandardItem *item)
0031 {
0032     if (item == mTextItem) {
0033         QString newText = item->data(Qt::EditRole).toString();
0034         mAccessibleObject.setText(newText);
0035     } else if (item == mValueItem) {
0036         bool couldConvert;
0037         const double value = item->data(Qt::EditRole).toDouble(&couldConvert);
0038         if (couldConvert) {
0039             mAccessibleObject.setCurrentValue(value);
0040         }
0041 
0042         mValueItem = nullptr; // Prevent recursion
0043         item->setData(mAccessibleObject.currentValue(), Qt::DisplayRole);
0044         mValueItem = item;
0045     }
0046 }
0047 
0048 QVariant ObjectPropertiesModel::headerData(int section, Qt::Orientation orientation, int role) const
0049 {
0050     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0051         switch (static_cast<ObjectPropertiesModelRoles>(section)) {
0052         case Name:
0053             return i18n("Property");
0054         case Value:
0055             return i18n("Value");
0056         }
0057     }
0058     return {};
0059 }
0060 
0061 QHash<int, QByteArray> ObjectPropertiesModel::roleNames() const
0062 {
0063     QHash<int, QByteArray> roles;
0064     roles[Name] = "name";
0065     roles[Value] = "value";
0066     return roles;
0067 }
0068 
0069 void ObjectPropertiesModel::setFontBold(QStandardItem *item)
0070 {
0071     QFont font = item->font();
0072     font.setBold(true);
0073     item->setFont(font);
0074 }
0075 
0076 void ObjectPropertiesModel::setAccessibleObject(const QAccessibleClient::AccessibleObject &acc)
0077 {
0078     beginResetModel();
0079     mAccessibleObject = acc;
0080     mTextItem = nullptr;
0081     mValueItem = nullptr;
0082     clear();
0083 
0084     if (!acc.isValid()) {
0085         endResetModel();
0086         return;
0087     }
0088 
0089     QAccessibleClient::AccessibleObject::Interfaces interfaces = acc.supportedInterfaces();
0090     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::AccessibleInterface)) {
0091         QStandardItem *item = append(i18n("Accessible"));
0092         setFontBold(item);
0093         append(i18n("Name"), acc.name(), item);
0094         append(i18n("Description"), acc.description(), item);
0095         append(i18n("Role"), acc.roleName(), item);
0096         append(i18n("LocalizedRole"), acc.localizedRoleName(), item);
0097         append(i18n("Visible"), acc.isVisible(), item);
0098         append(i18n("Default"), acc.isDefault(), item);
0099         append(i18n("State"), acc.stateString(), item);
0100         append(i18n("AccessibleId"), acc.accessibleId(), item);
0101         append(i18n("Url"), acc.url(), item);
0102         AccessibleObject parent = acc.parent();
0103         if (parent.isValid())
0104             append(i18n("Parent"), parent.url(), item);
0105         int childCount = acc.childCount();
0106         QStandardItem *children = append(i18n("Children"), acc.childCount(), item);
0107         for (int i = 0; i < childCount; ++i) {
0108             AccessibleObject child = acc.child(i);
0109             if (!child.isValid()) {
0110                 append(i18n("Broken child"), QString::number(i), children);
0111             } else {
0112                 append(child.name().isEmpty() ? QStringLiteral("[%1]").arg(child.roleName()) : child.name(), child.url(), children);
0113             }
0114         }
0115         // GetAttributes
0116     }
0117     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::ComponentInterface)) {
0118         QStandardItem *item = append(i18n("Component"));
0119         setFontBold(item);
0120         append(i18n("BoundingRect"), acc.boundingRect(), item);
0121         append(i18n("Layer"), acc.layer(), item);
0122         append(i18n("MDIZOrder"), acc.mdiZOrder(), item);
0123         append(i18n("Alpha"), acc.alpha(), item);
0124     }
0125     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::CollectionInterface)) {
0126         QStandardItem *item = append(i18n("Collection"));
0127         setFontBold(item);
0128         Q_UNUSED(item);
0129     }
0130     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::ApplicationInterface)) {
0131         QStandardItem *item = append(i18n("Application"));
0132         setFontBold(item);
0133         append(i18n("ToolkitName"), acc.appToolkitName(), item);
0134         append(i18n("Version"), acc.appVersion(), item);
0135         append(i18n("Id"), acc.appId(), item);
0136         append(i18n("Locale"), acc.appLocale(), item);
0137         append(i18n("BusAddress"), acc.appBusAddress(), item);
0138     }
0139     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::DocumentInterface)) {
0140         QStandardItem *item = append(i18n("Document"));
0141         setFontBold(item);
0142         Q_UNUSED(item);
0143         // GetLocale
0144         // GetAttributeValue
0145         // GetAttributes
0146     }
0147 
0148     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EditableTextInterface)) {
0149         QStandardItem *item = append(i18n("Editable Text"));
0150         setFontBold(item);
0151         Q_UNUSED(item);
0152     }
0153     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::HyperlinkInterface)) {
0154         QStandardItem *item = append(i18n("Hyperlink"));
0155         setFontBold(item);
0156         Q_UNUSED(item);
0157         /*
0158         <property name="NAnchors" type="n" access="read"/>
0159         <property name="StartIndex" type="i" access="read"/>
0160         <property name="EndIndex" type="i" access="read"/>
0161         <method name="GetObject">
0162             <arg direction="in" name="i" type="i"/>
0163             <arg direction="out" type="(so)"/>
0164             <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QSpiObjectReference"/>
0165         </method>
0166         0<method name="GetURI">
0167             <arg direction="in" name="i" type="i"/>
0168             <arg direction="out" type="s"/>
0169         </method>
0170         <method name="IsValid">
0171             <arg direction="out" type="b"/>
0172         </method>
0173         */
0174     }
0175     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::HypertextInterface)) {
0176         QStandardItem *item = append(i18n("Hypertext"));
0177         setFontBold(item);
0178         Q_UNUSED(item);
0179         /*
0180         <method name="GetNLinks">
0181             <arg direction="out" type="i"/>
0182         </method>
0183         <method name="GetLink">
0184             <arg direction="in" name="linkIndex" type="i"/>
0185             <arg direction="out" type="(so)"/>
0186             <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QSpiObjectReference"/>
0187         </method>
0188         <method name="GetLinkIndex">
0189             <arg direction="in" name="characterIndex" type="i"/>
0190             <arg direction="out" type="i"/>
0191         </method>
0192         */
0193     }
0194     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::ImageInterface)) {
0195         QStandardItem *item = append(i18n("Image"));
0196         setFontBold(item);
0197         append(i18n("Description"), acc.imageDescription(), item);
0198         append(i18n("Locale"), acc.imageLocale(), item);
0199         append(i18n("Rect"), acc.imageRect(), item);
0200     }
0201     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::SelectionInterface)) {
0202         QStandardItem *item = append(i18n("Selection"));
0203         setFontBold(item);
0204         for (const QAccessibleClient::AccessibleObject &s : acc.selection()) {
0205             append(s.name(), s.role(), item);
0206         }
0207     }
0208     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::TableInterface)) {
0209         QStandardItem *item = append(i18n("Table"));
0210         setFontBold(item);
0211         Q_UNUSED(item);
0212         /*
0213         <property name="NRows" type="i" access="read"/>
0214         <property name="NColumns" type="i" access="read"/>
0215         <property name="Caption" type="(so)" access="read">
0216             <annotation name="com.trolltech.QtDBus.QtTypeName" value="QSpiObjectReference"/>
0217         </property>
0218         <property name="Summary" type="(so)" access="read">
0219             <annotation name="com.trolltech.QtDBus.QtTypeName" value="QSpiObjectReference"/>
0220         </property>
0221         <property name="NSelectedRows" type="i" access="read"/>
0222         <property name="NSelectedColumns" type="i" access="read"/>
0223         <method name="GetRowDescription">
0224             <arg direction="in" name="row" type="i"/>
0225             <arg direction="out" type="s"/>
0226         </method>
0227         <method name="GetColumnDescription">
0228             <arg direction="in" name="column" type="i"/>
0229             <arg direction="out" type="s"/>
0230         </method>
0231         <method name="GetSelectedRows">
0232             <arg direction="out" type="ai"/>
0233             <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QSpiIntList"/>
0234         </method>
0235         <method name="GetSelectedColumns">
0236             <arg direction="out" type="ai"/>
0237             <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QSpiIntList"/>
0238         </method>
0239         */
0240     }
0241     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::TextInterface)) {
0242         QStandardItem *item = append(i18n("Text"));
0243         setFontBold(item);
0244         int offset = acc.caretOffset();
0245         append(i18n("Caret Offset"), offset, item);
0246         append(i18n("Character Count"), acc.characterCount(), item);
0247         append(i18n("Character Rect"), acc.characterRect(offset), item);
0248 
0249         QString text = acc.text();
0250         if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EditableTextInterface)) {
0251             append(i18n("Text"), text, item, &mTextItem);
0252         } else {
0253             append(i18n("Text"), text, item);
0254         }
0255 
0256         QList<QPair<int, int>> selections = acc.textSelections();
0257         QStandardItem *selectionsItem = append(i18n("Selections"), selections.count(), item);
0258         for (int i = 0; i < selections.count(); ++i) {
0259             QPair<int, int> sel = selections[i];
0260             int startOffset = sel.first;
0261             int endOffset = sel.second;
0262             Q_ASSERT(startOffset <= endOffset);
0263             append(QStringLiteral("%1:%2").arg(startOffset).arg(endOffset), text.mid(startOffset, endOffset - startOffset), selectionsItem);
0264         }
0265     }
0266     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::ValueInterface)) {
0267         QStandardItem *item = append(i18n("Value"));
0268         setFontBold(item);
0269         append(i18n("Current"), acc.currentValue(), item, &mValueItem);
0270         append(i18n("Minimum"), acc.minimumValue(), item);
0271         append(i18n("Maximum"), acc.maximumValue(), item);
0272         append(i18n("Increment"), acc.minimumValueIncrement(), item);
0273     }
0274     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::SocketInterface)) {
0275         QStandardItem *item = append(i18n("Socket"));
0276         setFontBold(item);
0277         Q_UNUSED(item);
0278     }
0279 
0280     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EventKeyboardInterface)) {
0281         QStandardItem *item = append(i18n("Event Keyboard"));
0282         setFontBold(item);
0283         Q_UNUSED(item);
0284     }
0285     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EventMouseInterface)) {
0286         QStandardItem *item = append(i18n("Event Mouse"));
0287         setFontBold(item);
0288         Q_UNUSED(item);
0289     }
0290     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EventObjectInterface)) {
0291         QStandardItem *item = append(i18n("Event Object"));
0292         setFontBold(item);
0293         Q_UNUSED(item);
0294     }
0295     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EventWindowInterface)) {
0296         QStandardItem *item = append(i18n("Event Window"));
0297         setFontBold(item);
0298         Q_UNUSED(item);
0299     }
0300     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::EventFocusInterface)) {
0301         QStandardItem *item = append(i18n("Event Focus"));
0302         setFontBold(item);
0303         Q_UNUSED(item);
0304     }
0305 
0306     if (interfaces.testFlag(QAccessibleClient::AccessibleObject::ActionInterface)) {
0307         if (!acc.actions().isEmpty()) {
0308             QStandardItem *item = append(i18n("Action"));
0309             setFontBold(item);
0310             for (const QSharedPointer<QAction> &a : acc.actions()) {
0311                 const QString shortCut = a->shortcut().toString();
0312                 QString text = a->text();
0313                 qDebug() << " a->shortcut() " << a->shortcut() << " text " << text;
0314                 if (!shortCut.isEmpty()) {
0315                     text += QStringLiteral(" (%1)").arg(shortCut);
0316                 }
0317                 auto nameItem = new QStandardItem(text);
0318                 auto valueItem = new QStandardItem(a->whatsThis());
0319                 nameItem->setEditable(false);
0320                 valueItem->setEditable(false);
0321                 item->appendRow(QList<QStandardItem *>() << nameItem << valueItem);
0322             }
0323         }
0324     }
0325 
0326     endResetModel();
0327 }
0328 
0329 AccessibleObject ObjectPropertiesModel::currentObject() const
0330 {
0331     return mAccessibleObject;
0332 }
0333 
0334 void ObjectPropertiesModel::doubleClicked(const QModelIndex &index)
0335 {
0336     if (!index.isValid() || !index.parent().isValid() || index.parent().data().toString() != i18n("Action")) // FIXME i18n ???
0337         return;
0338 
0339     const auto actions{mAccessibleObject.actions()};
0340     for (const QSharedPointer<QAction> &action : actions) {
0341         if (action->text() == data(index).toString()) {
0342             action->trigger();
0343             return;
0344         }
0345     }
0346 }
0347 
0348 QStandardItem *ObjectPropertiesModel::append(const QString &name, const QVariant &value, QStandardItem *parentItem, QStandardItem **changeHandler)
0349 {
0350     if (!parentItem)
0351         parentItem = invisibleRootItem();
0352     auto nameItem = new QStandardItem(name);
0353     QString text;
0354     switch (value.metaType().id()) {
0355     case QMetaType::QPoint: {
0356         const QPoint p = value.toPoint();
0357         text = QStringLiteral("%1:%2").arg(p.x()).arg(p.y());
0358         break;
0359     }
0360     case QMetaType::QPointF: {
0361         const QPointF p = value.toPointF();
0362         text = QStringLiteral("%1:%2").arg(p.x()).arg(p.y());
0363         break;
0364     }
0365     case QMetaType::QRect: {
0366         const QRect r = value.toRect();
0367         text = QStringLiteral("%1:%2 %3x%4").arg(r.left()).arg(r.top()).arg(r.width()).arg(r.height());
0368         break;
0369     }
0370     case QMetaType::QRectF: {
0371         const QRectF r = value.toRectF();
0372         text = QStringLiteral("%1:%2 %3x%4").arg(r.left()).arg(r.top()).arg(r.width()).arg(r.height());
0373         break;
0374     }
0375     default:
0376         text = value.toString();
0377         break;
0378     }
0379     auto valueItem = new QStandardItem(text);
0380     parentItem->appendRow(QList<QStandardItem *>() << nameItem << valueItem);
0381     nameItem->setEditable(false);
0382 
0383     if (changeHandler) {
0384         *changeHandler = valueItem;
0385         valueItem->setEditable(true);
0386     } else {
0387         valueItem->setEditable(false);
0388     }
0389 
0390     return nameItem;
0391 }
0392 
0393 #include "moc_accessiblepropertiesmodel.cpp"