File indexing completed on 2024-07-14 15:04:07

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 "accessibleobject.h"
0008 #include "qaccessibilityclient_debug.h"
0009 
0010 #include <QString>
0011 #include <QDebug>
0012 
0013 #include "accessibleobject_p.h"
0014 #include "registry_p.h"
0015 
0016 #include <atspi/atspi-constants.h>
0017 
0018 using namespace QAccessibleClient;
0019 
0020 AccessibleObject::AccessibleObject()
0021     :d(nullptr)
0022 {
0023 }
0024 
0025 AccessibleObject::AccessibleObject(RegistryPrivate *registryPrivate, const QString &service, const QString &path)
0026     :d(nullptr)
0027 {
0028     Q_ASSERT(registryPrivate);
0029     Q_ASSERT(!service.isEmpty());
0030     Q_ASSERT(!path.isEmpty());
0031     if (registryPrivate->m_cache) {
0032         const QString id = path + service;
0033         d = registryPrivate->m_cache->get(id);
0034         if (!d) {
0035             d = QSharedPointer<AccessibleObjectPrivate>(new AccessibleObjectPrivate(registryPrivate, service, path));
0036             registryPrivate->m_cache->add(id, d);
0037         }
0038     } else {
0039         d = QSharedPointer<AccessibleObjectPrivate>(new AccessibleObjectPrivate(registryPrivate, service, path));
0040     }
0041 }
0042 
0043 AccessibleObject::AccessibleObject(const QSharedPointer<AccessibleObjectPrivate> &dd)
0044     :d(dd)
0045 {
0046 }
0047 
0048 AccessibleObject::AccessibleObject(const AccessibleObject &other)
0049     : d(other.d)
0050 {
0051 }
0052 
0053 AccessibleObject::~AccessibleObject()
0054 {
0055 }
0056 
0057 QString AccessibleObject::id() const
0058 {
0059     if (!d || !d->registryPrivate)
0060         return QString();
0061     return d->path + d->service;
0062 }
0063 
0064 QUrl AccessibleObject::url() const
0065 {
0066     if (!d || !d->registryPrivate)
0067         return QUrl();
0068     QUrl u;
0069     u.setScheme(d->registryPrivate->ACCESSIBLE_OBJECT_SCHEME_STRING);
0070     u.setPath(d->path);
0071     u.setFragment(d->service);
0072     return u;
0073 }
0074 
0075 bool AccessibleObject::isValid() const
0076 {
0077     return d && d->registryPrivate
0078              && (!d->service.isEmpty())
0079              && (!d->path.isEmpty())
0080              && (d->path != QLatin1String("/org/a11y/atspi/null"));
0081 }
0082 
0083 AccessibleObject &AccessibleObject::operator=(const AccessibleObject &other)
0084 {
0085     d = other.d;
0086     return *this;
0087 }
0088 
0089 bool AccessibleObject::operator==(const AccessibleObject &other) const
0090 {
0091     return (d == other.d) || (d && other.d && *d == *other.d);
0092 }
0093 
0094 AccessibleObject AccessibleObject::parent() const
0095 {
0096     return d->registryPrivate->parentAccessible(*this);
0097 }
0098 
0099 QList<AccessibleObject> AccessibleObject::children() const
0100 {
0101     return d->registryPrivate->children(*this);
0102 }
0103 
0104 QVector< QList<AccessibleObject> > AccessibleObject::children(const QList<Role> &roles) const
0105 {
0106     QVector< QList<AccessibleObject> > result(roles.count());
0107     const QList<AccessibleObject> all = children();
0108     for(int i = 0; i < all.count(); ++i) {
0109         const AccessibleObject &child = all[i];
0110         int index = roles.indexOf(child.role());
0111         if (index < 0) continue;
0112         result[index].append(child);
0113     }
0114     return result;
0115 }
0116 
0117 int AccessibleObject::childCount() const
0118 {
0119     return d->registryPrivate->childCount(*this);
0120 }
0121 
0122 AccessibleObject AccessibleObject::child(int index) const
0123 {
0124     return d->registryPrivate->child(*this, index);
0125 }
0126 
0127 int AccessibleObject::indexInParent() const
0128 {
0129     return d->registryPrivate->indexInParent(*this);
0130 }
0131 
0132 QString AccessibleObject::accessibleId() const
0133 {
0134     return d->registryPrivate->accessibleId(*this);
0135 }
0136 
0137 QString AccessibleObject::name() const
0138 {
0139     return d->registryPrivate->name(*this);
0140 }
0141 
0142 QString AccessibleObject::description() const
0143 {
0144     return d->registryPrivate->description(*this);
0145 }
0146 
0147 AccessibleObject::Role AccessibleObject::role() const
0148 {
0149     return d->registryPrivate->role(*this);
0150 }
0151 
0152 QString AccessibleObject::roleName() const
0153 {
0154     return d->registryPrivate->roleName(*this);
0155 }
0156 
0157 QString AccessibleObject::localizedRoleName() const
0158 {
0159     return d->registryPrivate->localizedRoleName(*this);
0160 }
0161 
0162 int AccessibleObject::layer() const
0163 {
0164     return d->registryPrivate->layer(*this);
0165 }
0166 
0167 int AccessibleObject::mdiZOrder() const
0168 {
0169     return d->registryPrivate->mdiZOrder(*this);
0170 }
0171 
0172 double AccessibleObject::alpha() const
0173 {
0174     return d->registryPrivate->alpha(*this);
0175 }
0176 
0177 QRect AccessibleObject::boundingRect() const
0178 {
0179     if( supportedInterfaces() & AccessibleObject::ComponentInterface ){
0180         return d->registryPrivate->boundingRect(*this);
0181     } else {
0182         qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "boundingRect called on accessible that does not implement component";
0183         return QRect();
0184     }
0185 }
0186 
0187 QRect AccessibleObject::characterRect(int offset) const
0188 {
0189     if( supportedInterfaces() & AccessibleObject::TextInterface ){
0190         return d->registryPrivate->characterRect(*this, offset);
0191     } else {
0192         qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "characterRect called on accessible that does not implement text";
0193         return QRect();
0194     }
0195 }
0196 
0197 AccessibleObject::Interfaces AccessibleObject::supportedInterfaces() const
0198 {
0199     return d->registryPrivate->supportedInterfaces(*this);
0200 }
0201 
0202 int AccessibleObject::caretOffset() const
0203 {
0204     if( supportedInterfaces() & AccessibleObject::TextInterface ){
0205         return d->registryPrivate->caretOffset(*this);
0206     } else {
0207         qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "caretOffset called on accessible that does not implement text";
0208         return 0;
0209     }
0210 }
0211 
0212 int AccessibleObject::characterCount() const
0213 {
0214     if( supportedInterfaces() & AccessibleObject::TextInterface ){
0215         return d->registryPrivate->characterCount(*this);
0216     } else {
0217         qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "characterCount called on accessible that does not implement text";
0218         return 0;
0219     }
0220 }
0221 
0222 QString AccessibleObject::text(int startOffset, int endOffset) const
0223 {
0224     if( supportedInterfaces() & AccessibleObject::TextInterface )
0225         return d->registryPrivate->text(*this, startOffset, endOffset);
0226     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "text called on accessible that does not implement text";
0227     return QString();
0228 }
0229 
0230 QString AccessibleObject::textWithBoundary(int offset, TextBoundary boundary, int *startOffset, int *endOffset) const
0231 {
0232     if (supportedInterfaces() & AccessibleObject::TextInterface)
0233         return d->registryPrivate->textWithBoundary(*this, offset, boundary, startOffset, endOffset);
0234     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "text called on accessible that does not implement text";
0235     return QString();
0236 }
0237 
0238 bool AccessibleObject::setText(const QString &text)
0239 {
0240     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0241         return d->registryPrivate->setText(*this, text);
0242     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "setText called on accessible that does not implement editableText";
0243     return false;
0244 }
0245 
0246 bool AccessibleObject::insertText(const QString &text, int position, int length)
0247 {
0248     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0249         return d->registryPrivate->insertText(*this, text, position, length);
0250     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "insertText called on accessible that does not implement editableText";
0251     return false;
0252 }
0253 
0254 bool AccessibleObject::copyText(int startPos, int endPos)
0255 {
0256     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0257         return d->registryPrivate->copyText(*this, startPos, endPos);
0258     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "copyText called on accessible that does not implement editableText";
0259     return false;
0260 }
0261 
0262 bool AccessibleObject::cutText(int startPos, int endPos)
0263 {
0264     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0265         return d->registryPrivate->cutText(*this, startPos, endPos);
0266     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "cutText called on accessible that does not implement editableText";
0267     return false;
0268 }
0269 
0270 bool AccessibleObject::deleteText(int startPos, int endPos)
0271 {
0272     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0273         return d->registryPrivate->deleteText(*this, startPos, endPos);
0274     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "deleteText called on accessible that does not implement editableText";
0275     return false;
0276 }
0277 
0278 bool AccessibleObject::pasteText(int position)
0279 {
0280     if( supportedInterfaces() & AccessibleObject::EditableTextInterface )
0281         return d->registryPrivate->pasteText(*this, position);
0282     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "pasteText called on accessible that does not implement editableText";
0283     return false;
0284 }
0285 
0286 QList< QPair<int,int> > AccessibleObject::textSelections() const
0287 {
0288     if(supportedInterfaces() & AccessibleObject::Text)
0289         return d->registryPrivate->textSelections(*this);
0290     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "textSelections called on accessible that does not implement text";
0291     return QList< QPair<int,int> >();
0292 }
0293 
0294 void AccessibleObject::setTextSelections(const QList< QPair<int,int> > &selections)
0295 {
0296     if(supportedInterfaces() & AccessibleObject::Text)
0297         return d->registryPrivate->setTextSelections(*this, selections);
0298     qCWarning(LIBQACCESSIBILITYCLIENT_LOG) << "setTextSelections called on accessible that does not implement text";
0299 }
0300 
0301 QPoint AccessibleObject::focusPoint() const
0302 {
0303     Interfaces ifaces = supportedInterfaces();
0304     if (ifaces & TextInterface) {
0305         const int offset = caretOffset();
0306         const QRect r = characterRect(offset);
0307         if (r.x() != 0 || r.y() != 0)
0308             return r.center();
0309     }
0310     if (ifaces & ComponentInterface) {
0311         const QRect r = boundingRect();
0312         if (!r.isNull())
0313             return r.center();
0314     }
0315     AccessibleObject p = parent();
0316     if (p.isValid())
0317         return p.focusPoint(); // recursive
0318     return QPoint();
0319 }
0320 
0321 AccessibleObject AccessibleObject::application() const
0322 {
0323     return d->registryPrivate->application(*this);
0324 }
0325 
0326 QString AccessibleObject::appToolkitName() const
0327 {
0328     return d->registryPrivate->appToolkitName(*this);
0329 }
0330 
0331 QString AccessibleObject::appVersion() const
0332 {
0333     return d->registryPrivate->appVersion(*this);
0334 }
0335 
0336 int AccessibleObject::appId() const
0337 {
0338     return d->registryPrivate->appId(*this);
0339 }
0340 
0341 QString AccessibleObject::appLocale(LocaleType lctype) const
0342 {
0343     return d->registryPrivate->appLocale(*this, lctype);
0344 }
0345 
0346 QString AccessibleObject::appBusAddress() const
0347 {
0348     return d->registryPrivate->appBusAddress(*this);
0349 }
0350 
0351 double AccessibleObject::minimumValue() const
0352 {
0353     return d->registryPrivate->minimumValue(*this);
0354 }
0355 
0356 double AccessibleObject::maximumValue() const
0357 {
0358     return d->registryPrivate->maximumValue(*this);
0359 }
0360 
0361 double AccessibleObject::minimumValueIncrement() const
0362 {
0363     return d->registryPrivate->minimumValueIncrement(*this);
0364 }
0365 
0366 double AccessibleObject::currentValue() const
0367 {
0368     return d->registryPrivate->currentValue(*this);
0369 }
0370 
0371 bool AccessibleObject::setCurrentValue(double value)
0372 {
0373     return d->registryPrivate->setCurrentValue(*this, value);
0374 }
0375 
0376 QList<AccessibleObject> AccessibleObject::selection() const
0377 {
0378     return d->registryPrivate->selection(*this);
0379 }
0380 
0381 QString AccessibleObject::imageDescription() const
0382 {
0383     return d->registryPrivate->imageDescription(*this);
0384 }
0385 
0386 QString AccessibleObject::imageLocale() const
0387 {
0388     return d->registryPrivate->imageLocale(*this);
0389 }
0390 
0391 QRect AccessibleObject::imageRect() const
0392 {
0393     return d->registryPrivate->imageRect(*this);
0394 }
0395 
0396 QVector< QSharedPointer<QAction> > AccessibleObject::actions() const
0397 {
0398     // Actions in atspi are supposed to be static what means they cannot change in
0399     // between (e.g. actions removed or added or edited) so we can safely just
0400     // fetch them only once and store the result for the life-time of the object,
0401     if (!d->actionsFetched) {
0402         d->actionsFetched = true;
0403         d->actions = d->registryPrivate->actions(*this);
0404     }
0405     return d->actions;
0406 }
0407 
0408 bool AccessibleObject::hasSelectableText() const
0409 {
0410     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SELECTABLE_TEXT);
0411 }
0412 
0413 bool AccessibleObject::hasToolTip() const
0414 {
0415     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_HAS_TOOLTIP);
0416 }
0417 
0418 bool AccessibleObject::isActive() const
0419 {
0420     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_ACTIVE);
0421 }
0422 
0423 bool AccessibleObject::isCheckable() const
0424 {
0425     //FIXME: Find better AccessibleObject::isCheckable
0426     //return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_);
0427 
0428     Role role = d->registryPrivate->role(*this);
0429     if (role == AccessibleObject::CheckBox ||
0430         role == AccessibleObject::CheckableMenuItem ||
0431         role == AccessibleObject::RadioButton ||
0432         role == AccessibleObject::RadioMenuItem ||
0433         role == AccessibleObject::ToggleButton)
0434             return true;
0435     return false;
0436 }
0437 
0438 bool AccessibleObject::isChecked() const
0439 {
0440     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_CHECKED);
0441 }
0442 
0443 bool AccessibleObject::isDefunct() const
0444 {
0445     return d->defunct;
0446 }
0447 
0448 bool AccessibleObject::isDefault() const
0449 {
0450     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_IS_DEFAULT);
0451 }
0452 
0453 bool AccessibleObject::isEditable() const
0454 {
0455     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_EDITABLE);
0456 }
0457 
0458 bool AccessibleObject::isEnabled() const
0459 {
0460     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_ENABLED);
0461 }
0462 
0463 bool AccessibleObject::isExpandable() const
0464 {
0465     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_EXPANDABLE);
0466 }
0467 
0468 bool AccessibleObject::isExpanded() const
0469 {
0470     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_EXPANDED);
0471 }
0472 
0473 bool AccessibleObject::isFocusable() const
0474 {
0475     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_FOCUSABLE);
0476 }
0477 
0478 bool AccessibleObject::isFocused() const
0479 {
0480     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_FOCUSED);
0481 }
0482 
0483 bool AccessibleObject::isMultiLine() const
0484 {
0485     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_MULTI_LINE);
0486 }
0487 
0488 bool AccessibleObject::isSelectable() const
0489 {
0490     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SELECTABLE);
0491 }
0492 
0493 bool AccessibleObject::isSelected() const
0494 {
0495     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SELECTED);
0496 }
0497 
0498 bool AccessibleObject::isSensitive() const
0499 {
0500     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SENSITIVE);
0501 }
0502 
0503 bool AccessibleObject::isSingleLine() const
0504 {
0505     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SINGLE_LINE);
0506 }
0507 
0508 QString AccessibleObject::stateString() const
0509 {
0510     QStringList s;
0511     if (isActive()) s << QStringLiteral("Active");
0512     if (isCheckable()) s << QStringLiteral("Checkable");
0513     if (isChecked()) s << QStringLiteral("Checked");
0514     if (isEditable()) s << QStringLiteral("Editable");
0515     if (isExpandable()) s << QStringLiteral("Expandable");
0516     if (isExpanded()) s << QStringLiteral("Expanded");
0517     if (isFocusable()) s << QStringLiteral("Focusable");
0518     if (isFocused()) s << QStringLiteral("Focused");
0519     if (isMultiLine()) s << QStringLiteral("MultiLine");
0520     if (isSelectable()) s << QStringLiteral("Selectable");
0521     if (isSelected()) s << QStringLiteral("Selected");
0522     if (isSensitive()) s << QStringLiteral("Sensitive");
0523     if (isSingleLine()) s << QStringLiteral("SingleLine");
0524     if (isEnabled()) s << QStringLiteral("Enabled");
0525     return s.join(QLatin1String(", "));
0526 }
0527 
0528 bool AccessibleObject::isVisible() const
0529 {
0530     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_VISIBLE);
0531 }
0532 
0533 bool AccessibleObject::supportsAutocompletion() const
0534 {
0535     return d->registryPrivate->state(*this) & (quint64(1) << ATSPI_STATE_SUPPORTS_AUTOCOMPLETION);
0536 }
0537 
0538 #ifndef QT_NO_DEBUG_STREAM
0539 QACCESSIBILITYCLIENT_EXPORT QDebug QAccessibleClient::operator<<(QDebug d, const AccessibleObject &object)
0540 {
0541     d.nospace();
0542     d << "AccessibleObject(";
0543     if (object.d) {
0544         d << "service=" << object.d->service;
0545         d << " path=" << object.d->path;
0546         d << " name=" << object.name();
0547     } else {
0548         d << "invalid";
0549     }
0550     d << ")";
0551     return d.space();
0552 }
0553 #endif