diff options
Diffstat (limited to 'vcl/qt5/QtAccessibleWidget.cxx')
-rw-r--r-- | vcl/qt5/QtAccessibleWidget.cxx | 1848 |
1 files changed, 1848 insertions, 0 deletions
diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx new file mode 100644 index 0000000000..7eadc33138 --- /dev/null +++ b/vcl/qt5/QtAccessibleWidget.cxx @@ -0,0 +1,1848 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <QtAccessibleWidget.hxx> + +#include <QtGui/QAccessibleInterface> + +#include <QtAccessibleEventListener.hxx> +#include <QtAccessibleRegistry.hxx> +#include <QtFrame.hxx> +#include <QtTools.hxx> +#include <QtWidget.hxx> +#include <QtXAccessible.hxx> + +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleScrollType.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleTableSelection.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include <comphelper/AccessibleImplementationHelper.hxx> +#include <o3tl/any.hxx> +#include <sal/log.hxx> +#include <vcl/accessibility/AccessibleTextAttributeHelper.hxx> + +using namespace css; +using namespace css::accessibility; +using namespace css::beans; +using namespace css::uno; + +QtAccessibleWidget::QtAccessibleWidget(const Reference<XAccessible> xAccessible, QObject* pObject) + : m_xAccessible(xAccessible) + , m_pObject(pObject) +{ + Reference<XAccessibleContext> xContext = xAccessible->getAccessibleContext(); + Reference<XAccessibleEventBroadcaster> xBroadcaster(xContext, UNO_QUERY); + if (xBroadcaster.is()) + { + Reference<XAccessibleEventListener> xListener(new QtAccessibleEventListener(this)); + xBroadcaster->addAccessibleEventListener(xListener); + } +} + +void QtAccessibleWidget::invalidate() +{ + QtAccessibleRegistry::remove(m_xAccessible); + m_xAccessible.clear(); +} + +Reference<XAccessibleContext> QtAccessibleWidget::getAccessibleContextImpl() const +{ + Reference<XAccessibleContext> xAc; + + if (m_xAccessible.is()) + { + try + { + xAc = m_xAccessible->getAccessibleContext(); + } + catch (css::lang::DisposedException /*ex*/) + { + SAL_WARN("vcl.qt", "Accessible context disposed already"); + } + // sometimes getAccessibleContext throws also RuntimeException if context is no longer alive + catch (css::uno::RuntimeException /*ex*/) + { + // so let's catch it here, cuz otherwise soffice falls flat on its face + // with FatalError and nothing else + SAL_WARN("vcl.qt", "Accessible context no longer alive"); + } + } + + return xAc; +} + +css::uno::Reference<css::accessibility::XAccessibleTable> +QtAccessibleWidget::getAccessibleTableForParent() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return nullptr; + + Reference<XAccessible> xParent = xAcc->getAccessibleParent(); + if (!xParent.is()) + return nullptr; + + Reference<XAccessibleContext> xParentContext = xParent->getAccessibleContext(); + if (!xParentContext.is()) + return nullptr; + + return Reference<XAccessibleTable>(xParentContext, UNO_QUERY); +} + +QWindow* QtAccessibleWidget::window() const +{ + assert(m_pObject); + if (m_pObject->isWidgetType()) + { + QWidget* pWidget = static_cast<QWidget*>(m_pObject); + QWidget* pWindow = pWidget->window(); + if (pWindow) + return pWindow->windowHandle(); + } + + QAccessibleInterface* pParent = parent(); + if (pParent) + return pParent->window(); + + return nullptr; +} + +int QtAccessibleWidget::childCount() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return 0; + + sal_Int64 nChildCount = xAc->getAccessibleChildCount(); + if (nChildCount > std::numeric_limits<int>::max()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::childCount: Child count exceeds maximum int value, " + "returning max int."); + nChildCount = std::numeric_limits<int>::max(); + } + + return nChildCount; +} + +int QtAccessibleWidget::indexOfChild(const QAccessibleInterface* pChild) const +{ + const QtAccessibleWidget* pAccessibleWidget = dynamic_cast<const QtAccessibleWidget*>(pChild); + if (!pAccessibleWidget) + { + SAL_WARN( + "vcl.qt", + "QtAccessibleWidget::indexOfChild called with child that is no QtAccessibleWidget"); + return -1; + } + + Reference<XAccessibleContext> xContext = pAccessibleWidget->getAccessibleContextImpl(); + if (!xContext.is()) + return -1; + + sal_Int64 nChildIndex = xContext->getAccessibleIndexInParent(); + if (nChildIndex > std::numeric_limits<int>::max()) + { + // use -2 when the child index is too large to fit into 32 bit to neither use the + // valid index of another child nor -1, which would e.g. make the Orca screen reader + // interpret the object as being a zombie + SAL_WARN("vcl.qt", + "QtAccessibleWidget::indexOfChild: Child index exceeds maximum int value, " + "returning -2."); + nChildIndex = -2; + } + return nChildIndex; +} + +namespace +{ +sal_Int16 lcl_matchQtTextBoundaryType(QAccessible::TextBoundaryType boundaryType) +{ + switch (boundaryType) + { + case QAccessible::CharBoundary: + return com::sun::star::accessibility::AccessibleTextType::CHARACTER; + case QAccessible::WordBoundary: + return com::sun::star::accessibility::AccessibleTextType::WORD; + case QAccessible::SentenceBoundary: + return com::sun::star::accessibility::AccessibleTextType::SENTENCE; + case QAccessible::ParagraphBoundary: + return com::sun::star::accessibility::AccessibleTextType::PARAGRAPH; + case QAccessible::LineBoundary: + return com::sun::star::accessibility::AccessibleTextType::LINE; + case QAccessible::NoBoundary: + // assert here, better handle it directly at call site + assert(false + && "No match for QAccessible::NoBoundary, handle it separately at call site."); + break; + default: + break; + } + + SAL_WARN("vcl.qt", "Unmatched text boundary type: " << boundaryType); + return -1; +} + +QAccessible::Relation lcl_matchUnoRelation(short relationType) +{ + // Qt semantics is the other way around + switch (relationType) + { +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + case AccessibleRelationType::CONTENT_FLOWS_FROM: + return QAccessible::FlowsTo; + case AccessibleRelationType::CONTENT_FLOWS_TO: + return QAccessible::FlowsFrom; +#endif + case AccessibleRelationType::CONTROLLED_BY: + return QAccessible::Controller; + case AccessibleRelationType::CONTROLLER_FOR: + return QAccessible::Controlled; +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + case AccessibleRelationType::DESCRIBED_BY: + return QAccessible::DescriptionFor; +#endif + case AccessibleRelationType::LABELED_BY: + return QAccessible::Label; + case AccessibleRelationType::LABEL_FOR: + return QAccessible::Labelled; + case AccessibleRelationType::INVALID: + case AccessibleRelationType::MEMBER_OF: + case AccessibleRelationType::SUB_WINDOW_OF: + case AccessibleRelationType::NODE_CHILD_OF: + default: + SAL_WARN("vcl.qt", "Unmatched relation: " << relationType); + return {}; + } +} + +void lcl_appendRelation(QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>* relations, + AccessibleRelation aRelation, QAccessible::Relation match) +{ + QAccessible::Relation aQRelation = lcl_matchUnoRelation(aRelation.RelationType); + // skip in case there's no Qt relation matching the filter + if (!(aQRelation & match)) + return; + + sal_uInt32 nTargetCount = aRelation.TargetSet.getLength(); + + for (sal_uInt32 i = 0; i < nTargetCount; i++) + { + Reference<XAccessible> xAccessible(aRelation.TargetSet[i], uno::UNO_QUERY); + relations->append( + { QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xAccessible)), + aQRelation }); + } +} +} + +QVector<QPair<QAccessibleInterface*, QAccessible::Relation>> +QtAccessibleWidget::relations(QAccessible::Relation match) const +{ + QVector<QPair<QAccessibleInterface*, QAccessible::Relation>> relations; + + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return relations; + + Reference<XAccessibleRelationSet> xRelationSet = xAc->getAccessibleRelationSet(); + if (xRelationSet.is()) + { + int count = xRelationSet->getRelationCount(); + for (int i = 0; i < count; i++) + { + AccessibleRelation aRelation = xRelationSet->getRelation(i); + lcl_appendRelation(&relations, aRelation, match); + } + } + + return relations; +} + +QAccessibleInterface* QtAccessibleWidget::focusChild() const +{ + /* if (m_pWindow->HasChildPathFocus()) + return QAccessible::queryAccessibleInterface( + new QtXAccessible(m_xAccessible->getAccessibleContext()->getAccessibleChild(index))); */ + return QAccessible::queryAccessibleInterface(object()); +} + +QRect QtAccessibleWidget::rect() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QRect(); + + Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY); + awt::Point aPoint = xAccessibleComponent->getLocationOnScreen(); + awt::Size aSize = xAccessibleComponent->getSize(); + + return QRect(aPoint.X, aPoint.Y, aSize.Width, aSize.Height); +} + +QAccessibleInterface* QtAccessibleWidget::parent() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + if (xAc->getAccessibleParent().is()) + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xAc->getAccessibleParent())); + + // go via the QObject hierarchy; some a11y objects like the application + // (at the root of the a11y hierarchy) are handled solely by Qt and have + // no LO-internal a11y objects associated with them + QObject* pObj = object(); + if (pObj && pObj->parent()) + return QAccessible::queryAccessibleInterface(pObj->parent()); + + // return app as parent for top-level objects + return QAccessible::queryAccessibleInterface(qApp); +} + +QAccessibleInterface* QtAccessibleWidget::child(int index) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + if (index < 0 || index >= xAc->getAccessibleChildCount()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::child called with invalid index: " << index); + return nullptr; + } + + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xAc->getAccessibleChild(index))); +} + +QString QtAccessibleWidget::text(QAccessible::Text text) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QString(); + + switch (text) + { + case QAccessible::Name: + return toQString(xAc->getAccessibleName()); + case QAccessible::Description: + case QAccessible::DebugDescription: + return toQString(xAc->getAccessibleDescription()); + case QAccessible::Value: + case QAccessible::Help: + case QAccessible::Accelerator: + case QAccessible::UserText: + default: + return QString("Unknown"); + } +} +QAccessible::Role QtAccessibleWidget::role() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QAccessible::NoRole; + + switch (xAc->getAccessibleRole()) + { + case AccessibleRole::UNKNOWN: + return QAccessible::NoRole; + case AccessibleRole::ALERT: + return QAccessible::AlertMessage; + case AccessibleRole::COLUMN_HEADER: + return QAccessible::ColumnHeader; + case AccessibleRole::CANVAS: + return QAccessible::Canvas; + case AccessibleRole::CHECK_BOX: + return QAccessible::CheckBox; + case AccessibleRole::CHECK_MENU_ITEM: + return QAccessible::MenuItem; + case AccessibleRole::COLOR_CHOOSER: + return QAccessible::ColorChooser; + case AccessibleRole::COMBO_BOX: + return QAccessible::ComboBox; + case AccessibleRole::DATE_EDITOR: + return QAccessible::EditableText; + case AccessibleRole::DESKTOP_ICON: + return QAccessible::Graphic; + case AccessibleRole::DESKTOP_PANE: + case AccessibleRole::DIRECTORY_PANE: + return QAccessible::Pane; + case AccessibleRole::DIALOG: + return QAccessible::Dialog; + case AccessibleRole::DOCUMENT: + return QAccessible::Document; + case AccessibleRole::EMBEDDED_OBJECT: + return QAccessible::UserRole; + case AccessibleRole::END_NOTE: + return QAccessible::Note; + case AccessibleRole::FILE_CHOOSER: + return QAccessible::Dialog; + case AccessibleRole::FILLER: + return QAccessible::Whitespace; + case AccessibleRole::FONT_CHOOSER: + return QAccessible::UserRole; + case AccessibleRole::FOOTER: + return QAccessible::Footer; + case AccessibleRole::FOOTNOTE: + return QAccessible::Note; + case AccessibleRole::FRAME: // top-level window with title bar + return QAccessible::Window; + case AccessibleRole::GLASS_PANE: + return QAccessible::UserRole; + case AccessibleRole::GRAPHIC: + return QAccessible::Graphic; + case AccessibleRole::GROUP_BOX: + return QAccessible::Grouping; + case AccessibleRole::HEADER: + return QAccessible::UserRole; + case AccessibleRole::HEADING: + return QAccessible::Heading; + case AccessibleRole::HYPER_LINK: + return QAccessible::Link; + case AccessibleRole::ICON: + return QAccessible::Graphic; + case AccessibleRole::INTERNAL_FRAME: + return QAccessible::UserRole; + case AccessibleRole::LABEL: + return QAccessible::StaticText; + case AccessibleRole::LAYERED_PANE: + return QAccessible::Pane; + case AccessibleRole::LIST: + return QAccessible::List; + case AccessibleRole::LIST_ITEM: + return QAccessible::ListItem; + case AccessibleRole::MENU: + case AccessibleRole::MENU_BAR: + return QAccessible::MenuBar; + case AccessibleRole::MENU_ITEM: + return QAccessible::MenuItem; + case AccessibleRole::NOTIFICATION: + return QAccessible::Notification; + case AccessibleRole::OPTION_PANE: + return QAccessible::Pane; + case AccessibleRole::PAGE_TAB: + return QAccessible::PageTab; + case AccessibleRole::PAGE_TAB_LIST: + return QAccessible::PageTabList; + case AccessibleRole::PANEL: + return QAccessible::Pane; + case AccessibleRole::PARAGRAPH: + case AccessibleRole::BLOCK_QUOTE: + return QAccessible::Paragraph; + case AccessibleRole::PASSWORD_TEXT: + // Qt API doesn't have a separate role to distinguish password edits, + // but a 'passwordEdit' state + return QAccessible::EditableText; + case AccessibleRole::POPUP_MENU: + return QAccessible::PopupMenu; + case AccessibleRole::PUSH_BUTTON: + return QAccessible::Button; + case AccessibleRole::PROGRESS_BAR: + return QAccessible::ProgressBar; + case AccessibleRole::RADIO_BUTTON: + return QAccessible::RadioButton; + case AccessibleRole::RADIO_MENU_ITEM: + return QAccessible::MenuItem; + case AccessibleRole::ROW_HEADER: + return QAccessible::RowHeader; + case AccessibleRole::ROOT_PANE: + return QAccessible::Pane; + case AccessibleRole::SCROLL_BAR: + return QAccessible::ScrollBar; + case AccessibleRole::SCROLL_PANE: + return QAccessible::Pane; + case AccessibleRole::SHAPE: + return QAccessible::Graphic; + case AccessibleRole::SEPARATOR: + return QAccessible::Separator; + case AccessibleRole::SLIDER: + return QAccessible::Slider; + case AccessibleRole::SPIN_BOX: + return QAccessible::SpinBox; + case AccessibleRole::SPLIT_PANE: + return QAccessible::Pane; + case AccessibleRole::STATUS_BAR: + return QAccessible::StatusBar; + case AccessibleRole::TABLE: + return QAccessible::Table; + case AccessibleRole::TABLE_CELL: + return QAccessible::Cell; + case AccessibleRole::TEXT: + return QAccessible::EditableText; + case AccessibleRole::TEXT_FRAME: + return QAccessible::UserRole; + case AccessibleRole::TOGGLE_BUTTON: + return QAccessible::Button; + case AccessibleRole::TOOL_BAR: + return QAccessible::ToolBar; + case AccessibleRole::TOOL_TIP: + return QAccessible::ToolTip; + case AccessibleRole::TREE: + return QAccessible::Tree; + case AccessibleRole::VIEW_PORT: + return QAccessible::UserRole; + case AccessibleRole::BUTTON_DROPDOWN: + return QAccessible::ButtonDropDown; + case AccessibleRole::BUTTON_MENU: + return QAccessible::ButtonMenu; + case AccessibleRole::CAPTION: + return QAccessible::StaticText; + case AccessibleRole::CHART: + return QAccessible::Chart; + case AccessibleRole::EDIT_BAR: + return QAccessible::Equation; + case AccessibleRole::FORM: + return QAccessible::Form; + case AccessibleRole::IMAGE_MAP: + return QAccessible::Graphic; + case AccessibleRole::NOTE: + return QAccessible::Note; + case AccessibleRole::RULER: + return QAccessible::UserRole; + case AccessibleRole::SECTION: + return QAccessible::Section; + case AccessibleRole::TREE_ITEM: + return QAccessible::TreeItem; + case AccessibleRole::TREE_TABLE: + return QAccessible::Tree; + case AccessibleRole::COMMENT: + return QAccessible::Note; + case AccessibleRole::COMMENT_END: + return QAccessible::UserRole; + case AccessibleRole::DOCUMENT_PRESENTATION: + return QAccessible::Document; + case AccessibleRole::DOCUMENT_SPREADSHEET: + return QAccessible::Document; + case AccessibleRole::DOCUMENT_TEXT: + return QAccessible::Document; + case AccessibleRole::STATIC: + return QAccessible::StaticText; + case AccessibleRole::WINDOW: // top-level window without title bar + return QAccessible::Window; + } + + SAL_WARN("vcl.qt", "Unmapped role: " << getAccessibleContextImpl()->getAccessibleRole()); + return QAccessible::NoRole; +} + +namespace +{ +void lcl_addState(QAccessible::State* state, sal_Int64 nState) +{ + switch (nState) + { + case AccessibleStateType::INVALID: + state->invalid = true; + break; + case AccessibleStateType::ACTIVE: + state->active = true; + break; + case AccessibleStateType::ARMED: + // No match + break; + case AccessibleStateType::BUSY: + state->busy = true; + break; + case AccessibleStateType::CHECKABLE: + state->checkable = true; + break; + case AccessibleStateType::CHECKED: + state->checked = true; + break; + case AccessibleStateType::EDITABLE: + state->editable = true; + break; + case AccessibleStateType::ENABLED: + state->disabled = false; + break; + case AccessibleStateType::EXPANDABLE: + state->expandable = true; + break; + case AccessibleStateType::EXPANDED: + state->expanded = true; + break; + case AccessibleStateType::FOCUSABLE: + state->focusable = true; + break; + case AccessibleStateType::FOCUSED: + state->focused = true; + break; + case AccessibleStateType::HORIZONTAL: + // No match + break; + case AccessibleStateType::ICONIFIED: + // No match + break; + case AccessibleStateType::INDETERMINATE: + state->checkStateMixed = true; + break; + case AccessibleStateType::MANAGES_DESCENDANTS: + // No match + break; + case AccessibleStateType::MODAL: + state->modal = true; + break; + case AccessibleStateType::MOVEABLE: + state->movable = true; + break; + case AccessibleStateType::MULTI_LINE: + state->multiLine = true; + break; + case AccessibleStateType::OPAQUE: + // No match + break; + case AccessibleStateType::PRESSED: + state->pressed = true; + break; + case AccessibleStateType::RESIZABLE: + state->sizeable = true; + break; + case AccessibleStateType::SELECTABLE: + state->selectable = true; + break; + case AccessibleStateType::SELECTED: + state->selected = true; + break; + case AccessibleStateType::SENSITIVE: + // No match + break; + case AccessibleStateType::SHOWING: + // No match + break; + case AccessibleStateType::SINGLE_LINE: + // No match + break; + case AccessibleStateType::STALE: + // No match + break; + case AccessibleStateType::TRANSIENT: + // No match + break; + case AccessibleStateType::VERTICAL: + // No match + break; + case AccessibleStateType::VISIBLE: + state->invisible = false; + break; + case AccessibleStateType::DEFAULT: + // No match + break; + case AccessibleStateType::DEFUNC: + state->invalid = true; + break; + case AccessibleStateType::MULTI_SELECTABLE: + state->multiSelectable = true; + break; + default: + SAL_WARN("vcl.qt", "Unmapped state: " << nState); + break; + } +} +} + +QAccessible::State QtAccessibleWidget::state() const +{ + QAccessible::State state; + + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return state; + + sal_Int64 nStateSet(xAc->getAccessibleStateSet()); + + for (int i = 0; i < 63; ++i) + { + sal_Int64 nState = sal_Int64(1) << i; + if (nStateSet & nState) + lcl_addState(&state, nState); + } + + if (xAc->getAccessibleRole() == AccessibleRole::PASSWORD_TEXT) + state.passwordEdit = true; + + return state; +} + +QColor QtAccessibleWidget::foregroundColor() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QColor(); + + Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY); + return toQColor(Color(ColorTransparency, xAccessibleComponent->getForeground())); +} + +QColor QtAccessibleWidget::backgroundColor() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QColor(); + + Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY); + return toQColor(Color(ColorTransparency, xAccessibleComponent->getBackground())); +} + +void* QtAccessibleWidget::interface_cast(QAccessible::InterfaceType t) +{ + if (t == QAccessible::ActionInterface && accessibleProvidesInterface<XAccessibleAction>()) + return static_cast<QAccessibleActionInterface*>(this); + if (t == QAccessible::TextInterface && accessibleProvidesInterface<XAccessibleText>()) + return static_cast<QAccessibleTextInterface*>(this); + if (t == QAccessible::EditableTextInterface + && accessibleProvidesInterface<XAccessibleEditableText>()) + return static_cast<QAccessibleEditableTextInterface*>(this); + if (t == QAccessible::ValueInterface && accessibleProvidesInterface<XAccessibleValue>()) + return static_cast<QAccessibleValueInterface*>(this); + if (t == QAccessible::TableCellInterface) + { + // parent must be a table + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (xTable.is()) + return static_cast<QAccessibleTableCellInterface*>(this); + } + if (t == QAccessible::TableInterface && accessibleProvidesInterface<XAccessibleTable>()) + return static_cast<QAccessibleTableInterface*>(this); +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + if (t == QAccessible::SelectionInterface && accessibleProvidesInterface<XAccessibleSelection>()) + return static_cast<QAccessibleSelectionInterface*>(this); +#endif + return nullptr; +} + +bool QtAccessibleWidget::isValid() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + return xAc.is(); +} + +QObject* QtAccessibleWidget::object() const { return m_pObject; } + +void QtAccessibleWidget::setText(QAccessible::Text /* t */, const QString& /* text */) {} + +QAccessibleInterface* QtAccessibleWidget::childAt(int x, int y) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY); + // convert from screen to local coordinates + QPoint aLocalCoords = QPoint(x, y) - rect().topLeft(); + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xAccessibleComponent->getAccessibleAtPoint( + awt::Point(aLocalCoords.x(), aLocalCoords.y())))); +} + +QAccessibleInterface* QtAccessibleWidget::customFactory(const QString& classname, QObject* object) +{ + if (classname == QLatin1String("QtWidget") && object && object->isWidgetType()) + { + QtWidget* pWidget = static_cast<QtWidget*>(object); + vcl::Window* pWindow = pWidget->frame().GetWindow(); + + if (pWindow) + { + css::uno::Reference<XAccessible> xAcc = pWindow->GetAccessible(); + // insert into registry so the association between the XAccessible and the QtWidget + // is remembered rather than creating a different QtXAccessible when a QObject is needed later + QtAccessibleRegistry::insert(xAcc, object); + return new QtAccessibleWidget(xAcc, object); + } + } + if (classname == QLatin1String("QtXAccessible") && object) + { + QtXAccessible* pXAccessible = static_cast<QtXAccessible*>(object); + if (pXAccessible->m_xAccessible.is()) + { + QtAccessibleWidget* pRet = new QtAccessibleWidget(pXAccessible->m_xAccessible, object); + // clear the reference in the QtXAccessible, no longer needed now that the QtAccessibleWidget holds one + pXAccessible->m_xAccessible.clear(); + return pRet; + } + } + + return nullptr; +} + +// QAccessibleActionInterface +QStringList QtAccessibleWidget::actionNames() const +{ + QStringList actionNames; + Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY); + if (!xAccessibleAction.is()) + return actionNames; + + int count = xAccessibleAction->getAccessibleActionCount(); + for (int i = 0; i < count; i++) + { + OUString desc = xAccessibleAction->getAccessibleActionDescription(i); + actionNames.append(toQString(desc)); + } + return actionNames; +} + +void QtAccessibleWidget::doAction(const QString& actionName) +{ + Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY); + if (!xAccessibleAction.is()) + return; + + int index = actionNames().indexOf(actionName); + if (index == -1) + return; + xAccessibleAction->doAccessibleAction(index); +} + +QStringList QtAccessibleWidget::keyBindingsForAction(const QString& actionName) const +{ + QStringList keyBindings; + Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY); + if (!xAccessibleAction.is()) + return keyBindings; + + int index = actionNames().indexOf(actionName); + if (index == -1) + return keyBindings; + + Reference<XAccessibleKeyBinding> xKeyBinding + = xAccessibleAction->getAccessibleActionKeyBinding(index); + + if (!xKeyBinding.is()) + return keyBindings; + + int count = xKeyBinding->getAccessibleKeyBindingCount(); + for (int i = 0; i < count; i++) + { + Sequence<awt::KeyStroke> keyStroke = xKeyBinding->getAccessibleKeyBinding(i); + keyBindings.append(toQString(comphelper::GetkeyBindingStrByXkeyBinding(keyStroke))); + } + return keyBindings; +} + +// QAccessibleTextInterface +void QtAccessibleWidget::addSelection(int /* startOffset */, int /* endOffset */) +{ + SAL_INFO("vcl.qt", "Unsupported QAccessibleTextInterface::addSelection"); +} + +// Text attributes are returned in format specified in IAccessible2 spec, since that +// is what Qt handles: +// https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes +QString QtAccessibleWidget::attributes(int offset, int* startOffset, int* endOffset) const +{ + if (startOffset == nullptr || endOffset == nullptr) + return QString(); + + *startOffset = -1; + *endOffset = -1; + + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QString(); + + // handle special values for offset the same way base class's QAccessibleTextWidget::attributes does + // (as defined in IAccessible 2: -1 -> length, -2 -> cursor position) + if (offset == -2) + offset = cursorPosition(); + + const int nTextLength = characterCount(); + if (offset == -1 || offset == nTextLength) + offset = nTextLength - 1; + + if (offset < 0 || offset > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::attributes called with invalid offset: " << offset); + return QString(); + } + + // Qt doesn't have the strict separation into text and object attributes, but also + // supports text-specific attributes that are object attributes according to the + // IAccessible2 spec. + sal_Int32 nStart = 0; + sal_Int32 nEnd = 0; + const OUString aRet = AccessibleTextAttributeHelper::GetIAccessible2TextAttributes( + xText, IA2AttributeType::TextAttributes | IA2AttributeType::ObjectAttributes, + static_cast<sal_Int32>(offset), nStart, nEnd); + *startOffset = static_cast<int>(nStart); + *endOffset = static_cast<int>(nEnd); + return toQString(aRet); +} + +int QtAccessibleWidget::characterCount() const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (xText.is()) + return xText->getCharacterCount(); + return 0; +} + +QRect QtAccessibleWidget::characterRect(int nOffset) const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QRect(); + + if (nOffset < 0 || nOffset > xText->getCharacterCount()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::characterRect called with invalid offset: " << nOffset); + return QRect(); + } + + const awt::Rectangle aBounds = xText->getCharacterBounds(nOffset); + const QRect aRect(aBounds.X, aBounds.Y, aBounds.Width, aBounds.Height); + // convert to screen coordinates + const QRect aScreenPos = rect(); + return aRect.translated(aScreenPos.x(), aScreenPos.y()); +} + +int QtAccessibleWidget::cursorPosition() const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (xText.is()) + return xText->getCaretPosition(); + return 0; +} + +int QtAccessibleWidget::offsetAtPoint(const QPoint& rPoint) const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return -1; + + // convert from screen to local coordinates + QPoint aLocalCoords = rPoint - rect().topLeft(); + awt::Point aPoint(aLocalCoords.x(), aLocalCoords.y()); + return xText->getIndexAtPoint(aPoint); +} + +void QtAccessibleWidget::removeSelection(int /* selectionIndex */) +{ + SAL_INFO("vcl.qt", "Unsupported QAccessibleTextInterface::removeSelection"); +} + +void QtAccessibleWidget::scrollToSubstring(int startIndex, int endIndex) +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return; + + sal_Int32 nTextLength = xText->getCharacterCount(); + if (startIndex < 0 || startIndex > nTextLength || endIndex < 0 || endIndex > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::scrollToSubstring called with invalid offset."); + return; + } + + xText->scrollSubstringTo(startIndex, endIndex, AccessibleScrollType_SCROLL_ANYWHERE); +} + +void QtAccessibleWidget::selection(int selectionIndex, int* startOffset, int* endOffset) const +{ + if (!startOffset && !endOffset) + return; + + Reference<XAccessibleText> xText; + if (selectionIndex == 0) + xText = Reference<XAccessibleText>(getAccessibleContextImpl(), UNO_QUERY); + + if (startOffset) + *startOffset = xText.is() ? xText->getSelectionStart() : 0; + if (endOffset) + *endOffset = xText.is() ? xText->getSelectionEnd() : 0; +} + +int QtAccessibleWidget::selectionCount() const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (xText.is() && !xText->getSelectedText().isEmpty()) + return 1; // Only 1 selection supported atm + return 0; +} + +void QtAccessibleWidget::setCursorPosition(int position) +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return; + + if (position < 0 || position > xText->getCharacterCount()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::setCursorPosition called with invalid offset: " << position); + return; + } + + xText->setCaretPosition(position); +} + +void QtAccessibleWidget::setSelection(int /* selectionIndex */, int startOffset, int endOffset) +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return; + + sal_Int32 nTextLength = xText->getCharacterCount(); + if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::setSelection called with invalid offset."); + return; + } + + xText->setSelection(startOffset, endOffset); +} + +QString QtAccessibleWidget::text(int startOffset, int endOffset) const +{ + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QString(); + + sal_Int32 nTextLength = xText->getCharacterCount(); + if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::text called with invalid offset."); + return QString(); + } + + return toQString(xText->getTextRange(startOffset, endOffset)); +} + +QString QtAccessibleWidget::textAfterOffset(int nOffset, + QAccessible::TextBoundaryType eBoundaryType, + int* pStartOffset, int* pEndOffset) const +{ + if (pStartOffset == nullptr || pEndOffset == nullptr) + return QString(); + + *pStartOffset = -1; + *pEndOffset = -1; + + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QString(); + + const int nCharCount = characterCount(); + // -1 is special value for text length + if (nOffset == -1) + nOffset = nCharCount; + else if (nOffset < -1 || nOffset > nCharCount) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::textAfterOffset called with invalid offset: " << nOffset); + return QString(); + } + + if (eBoundaryType == QAccessible::NoBoundary) + { + if (nOffset == nCharCount) + return QString(); + *pStartOffset = nOffset + 1; + *pEndOffset = nCharCount; + return text(nOffset + 1, nCharCount); + } + + sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(eBoundaryType); + assert(nUnoBoundaryType > 0); + const TextSegment aSegment = xText->getTextBehindIndex(nOffset, nUnoBoundaryType); + *pStartOffset = aSegment.SegmentStart; + *pEndOffset = aSegment.SegmentEnd; + return toQString(aSegment.SegmentText); +} + +QString QtAccessibleWidget::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int* startOffset, int* endOffset) const +{ + if (startOffset == nullptr || endOffset == nullptr) + return QString(); + + const int nCharCount = characterCount(); + if (boundaryType == QAccessible::NoBoundary) + { + *startOffset = 0; + *endOffset = nCharCount; + return text(0, nCharCount); + } + + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QString(); + + sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(boundaryType); + assert(nUnoBoundaryType > 0); + + // special value of -1 for offset means text length + if (offset == -1) + offset = nCharCount; + + if (offset < 0 || offset > nCharCount) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::textAtOffset called with invalid offset: " << offset); + return QString(); + } + + const TextSegment segment = xText->getTextAtIndex(offset, nUnoBoundaryType); + *startOffset = segment.SegmentStart; + *endOffset = segment.SegmentEnd; + return toQString(segment.SegmentText); +} + +QString QtAccessibleWidget::textBeforeOffset(int nOffset, + QAccessible::TextBoundaryType eBoundaryType, + int* pStartOffset, int* pEndOffset) const +{ + if (pStartOffset == nullptr || pEndOffset == nullptr) + return QString(); + + *pStartOffset = -1; + *pEndOffset = -1; + + Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY); + if (!xText.is()) + return QString(); + + const int nCharCount = characterCount(); + // -1 is special value for text length + if (nOffset == -1) + nOffset = nCharCount; + else if (nOffset < -1 || nOffset > nCharCount) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::textBeforeOffset called with invalid offset: " << nOffset); + return QString(); + } + + if (eBoundaryType == QAccessible::NoBoundary) + { + *pStartOffset = 0; + *pEndOffset = nOffset; + return text(0, nOffset); + } + + sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(eBoundaryType); + assert(nUnoBoundaryType > 0); + const TextSegment aSegment = xText->getTextBeforeIndex(nOffset, nUnoBoundaryType); + *pStartOffset = aSegment.SegmentStart; + *pEndOffset = aSegment.SegmentEnd; + return toQString(aSegment.SegmentText); +} + +// QAccessibleEditableTextInterface + +void QtAccessibleWidget::deleteText(int startOffset, int endOffset) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return; + + Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY); + if (!xEditableText.is()) + return; + + sal_Int32 nTextLength = xEditableText->getCharacterCount(); + if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::deleteText called with invalid offset."); + return; + } + + xEditableText->deleteText(startOffset, endOffset); +} + +void QtAccessibleWidget::insertText(int offset, const QString& text) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return; + + Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY); + if (!xEditableText.is()) + return; + + if (offset < 0 || offset > xEditableText->getCharacterCount()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::insertText called with invalid offset: " << offset); + return; + } + + xEditableText->insertText(toOUString(text), offset); +} + +void QtAccessibleWidget::replaceText(int startOffset, int endOffset, const QString& text) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return; + + Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY); + if (!xEditableText.is()) + return; + + sal_Int32 nTextLength = xEditableText->getCharacterCount(); + if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::replaceText called with invalid offset."); + return; + } + + xEditableText->replaceText(startOffset, endOffset, toOUString(text)); +} + +// QAccessibleValueInterface +QVariant QtAccessibleWidget::currentValue() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QVariant(); + + Reference<XAccessibleValue> xValue(xAc, UNO_QUERY); + if (!xValue.is()) + return QVariant(); + double aDouble = 0; + xValue->getCurrentValue() >>= aDouble; + return QVariant(aDouble); +} + +QVariant QtAccessibleWidget::maximumValue() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QVariant(); + + Reference<XAccessibleValue> xValue(xAc, UNO_QUERY); + if (!xValue.is()) + return QVariant(); + double aDouble = 0; + xValue->getMaximumValue() >>= aDouble; + return QVariant(aDouble); +} + +QVariant QtAccessibleWidget::minimumStepSize() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QVariant(); + + Reference<XAccessibleValue> xValue(xAc, UNO_QUERY); + if (!xValue.is()) + return QVariant(); + double dMinStep = 0; + xValue->getMinimumIncrement() >>= dMinStep; + return QVariant(dMinStep); +} + +QVariant QtAccessibleWidget::minimumValue() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QVariant(); + + Reference<XAccessibleValue> xValue(xAc, UNO_QUERY); + if (!xValue.is()) + return QVariant(); + double aDouble = 0; + xValue->getMinimumValue() >>= aDouble; + return QVariant(aDouble); +} + +void QtAccessibleWidget::setCurrentValue(const QVariant& value) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return; + + Reference<XAccessibleValue> xValue(xAc, UNO_QUERY); + if (!xValue.is()) + return; + + // Different types of numerical values for XAccessibleValue are possible. + // If current value has an integer type, also use that for the new value, to make + // sure underlying implementations expecting that can handle the value properly. + const Any aCurrentValue = xValue->getCurrentValue(); + if (aCurrentValue.getValueTypeClass() == css::uno::TypeClass::TypeClass_LONG) + xValue->setCurrentValue(Any(static_cast<sal_Int32>(value.toInt()))); + else if (aCurrentValue.getValueTypeClass() == css::uno::TypeClass::TypeClass_HYPER) + xValue->setCurrentValue(Any(static_cast<sal_Int64>(value.toLongLong()))); + else + xValue->setCurrentValue(Any(value.toDouble())); +} + +// QAccessibleTableInterface +QAccessibleInterface* QtAccessibleWidget::caption() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return nullptr; + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xTable->getAccessibleCaption())); +} + +QAccessibleInterface* QtAccessibleWidget::cellAt(int row, int column) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return nullptr; + + if (row < 0 || row >= xTable->getAccessibleRowCount() || column < 0 + || column >= xTable->getAccessibleColumnCount()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::cellAt called with invalid row/column index (" + << row << ", " << column << ")"); + return nullptr; + } + + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xTable->getAccessibleCellAt(row, column))); +} + +int QtAccessibleWidget::columnCount() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return 0; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return 0; + return xTable->getAccessibleColumnCount(); +} + +QString QtAccessibleWidget::columnDescription(int column) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QString(); + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return QString(); + return toQString(xTable->getAccessibleColumnDescription(column)); +} + +bool QtAccessibleWidget::isColumnSelected(int nColumn) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return false; + + if (nColumn < 0 || nColumn >= xTable->getAccessibleColumnCount()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::isColumnSelected called with invalid column index " + << nColumn); + return false; + } + + return xTable->isAccessibleColumnSelected(nColumn); +} + +bool QtAccessibleWidget::isRowSelected(int nRow) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return false; + + if (nRow < 0 || nRow >= xTable->getAccessibleRowCount()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::isRowSelected called with invalid row index " << nRow); + return false; + } + + return xTable->isAccessibleRowSelected(nRow); +} + +void QtAccessibleWidget::modelChange(QAccessibleTableModelChangeEvent*) {} + +int QtAccessibleWidget::rowCount() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return 0; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return 0; + return xTable->getAccessibleRowCount(); +} + +QString QtAccessibleWidget::rowDescription(int row) const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QString(); + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return QString(); + return toQString(xTable->getAccessibleRowDescription(row)); +} + +bool QtAccessibleWidget::selectColumn(int column) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + if (column < 0 || column >= columnCount()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::selectColumn called with invalid column index " << column); + return false; + } + + Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY); + if (!xTableSelection.is()) + return false; + return xTableSelection->selectColumn(column); +} + +bool QtAccessibleWidget::selectRow(int row) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + if (row < 0 || row >= rowCount()) + { + SAL_WARN("vcl.qt", "QtAccessibleWidget::selectRow called with invalid row index " << row); + return false; + } + + Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY); + if (!xTableSelection.is()) + return false; + return xTableSelection->selectRow(row); +} + +int QtAccessibleWidget::selectedCellCount() const { return selectedItemCount(); } + +QList<QAccessibleInterface*> QtAccessibleWidget::selectedCells() const { return selectedItems(); } + +int QtAccessibleWidget::selectedColumnCount() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return 0; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return 0; + return xTable->getSelectedAccessibleColumns().getLength(); +} + +QList<int> QtAccessibleWidget::selectedColumns() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QList<int>(); + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return QList<int>(); + return toQList(xTable->getSelectedAccessibleColumns()); +} + +int QtAccessibleWidget::selectedRowCount() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return 0; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return 0; + return xTable->getSelectedAccessibleRows().getLength(); +} + +QList<int> QtAccessibleWidget::selectedRows() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return QList<int>(); + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return QList<int>(); + return toQList(xTable->getSelectedAccessibleRows()); +} + +QAccessibleInterface* QtAccessibleWidget::summary() const +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return nullptr; + + Reference<XAccessibleTable> xTable(xAc, UNO_QUERY); + if (!xTable.is()) + return nullptr; + return QAccessible::queryAccessibleInterface( + QtAccessibleRegistry::getQObject(xTable->getAccessibleSummary())); +} + +bool QtAccessibleWidget::unselectColumn(int column) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY); + if (!xTableSelection.is()) + return false; + return xTableSelection->unselectColumn(column); +} + +bool QtAccessibleWidget::unselectRow(int row) +{ + Reference<XAccessibleContext> xAc = getAccessibleContextImpl(); + if (!xAc.is()) + return false; + + Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY); + if (!xTableSelection.is()) + return false; + return xTableSelection->unselectRow(row); +} + +// QAccessibleTableCellInterface +QList<QAccessibleInterface*> QtAccessibleWidget::columnHeaderCells() const +{ + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return QList<QAccessibleInterface*>(); + + Reference<XAccessibleTable> xHeaders = xTable->getAccessibleColumnHeaders(); + if (!xHeaders.is()) + return QList<QAccessibleInterface*>(); + + const sal_Int32 nCol = columnIndex(); + QList<QAccessibleInterface*> aHeaderCells; + for (sal_Int32 nRow = 0; nRow < xHeaders->getAccessibleRowCount(); nRow++) + { + Reference<XAccessible> xCell = xHeaders->getAccessibleCellAt(nRow, nCol); + QAccessibleInterface* pInterface + = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xCell)); + aHeaderCells.push_back(pInterface); + } + return aHeaderCells; +} + +int QtAccessibleWidget::columnIndex() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return -1; + + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return -1; + + const sal_Int64 nIndexInParent = xAcc->getAccessibleIndexInParent(); + return xTable->getAccessibleColumn(nIndexInParent); +} + +bool QtAccessibleWidget::isSelected() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return false; + + const sal_Int32 nColumn = columnIndex(); + const sal_Int32 nRow = rowIndex(); + return xTable->isAccessibleSelected(nRow, nColumn); +} + +int QtAccessibleWidget::columnExtent() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return -1; + + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return -1; + + const sal_Int32 nColumn = columnIndex(); + const sal_Int32 nRow = rowIndex(); + return xTable->getAccessibleColumnExtentAt(nRow, nColumn); +} + +QList<QAccessibleInterface*> QtAccessibleWidget::rowHeaderCells() const +{ + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return QList<QAccessibleInterface*>(); + + Reference<XAccessibleTable> xHeaders = xTable->getAccessibleRowHeaders(); + if (!xHeaders.is()) + return QList<QAccessibleInterface*>(); + + const sal_Int32 nRow = rowIndex(); + QList<QAccessibleInterface*> aHeaderCells; + for (sal_Int32 nCol = 0; nCol < xHeaders->getAccessibleColumnCount(); nCol++) + { + Reference<XAccessible> xCell = xHeaders->getAccessibleCellAt(nRow, nCol); + QAccessibleInterface* pInterface + = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xCell)); + aHeaderCells.push_back(pInterface); + } + return aHeaderCells; +} + +int QtAccessibleWidget::rowExtent() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return -1; + + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return -1; + + const sal_Int32 nColumn = columnIndex(); + const sal_Int32 nRow = rowIndex(); + return xTable->getAccessibleRowExtentAt(nRow, nColumn); +} + +int QtAccessibleWidget::rowIndex() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return -1; + + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return -1; + + const sal_Int64 nIndexInParent = xAcc->getAccessibleIndexInParent(); + return xTable->getAccessibleRow(nIndexInParent); +} + +QAccessibleInterface* QtAccessibleWidget::table() const +{ + Reference<XAccessibleTable> xTable = getAccessibleTableForParent(); + if (!xTable.is()) + return nullptr; + + Reference<XAccessible> xTableAcc(xTable, UNO_QUERY); + if (!xTableAcc.is()) + return nullptr; + + return QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xTableAcc)); +} + +// QAccessibleSelectionInterface +int QtAccessibleWidget::selectedItemCount() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return 0; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return 0; + + sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount(); + if (nSelected > std::numeric_limits<int>::max()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::selectedItemCount: Cell count exceeds maximum int value, " + "using max int."); + nSelected = std::numeric_limits<int>::max(); + } + return nSelected; +} + +QList<QAccessibleInterface*> QtAccessibleWidget::selectedItems() const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return QList<QAccessibleInterface*>(); + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return QList<QAccessibleInterface*>(); + + QList<QAccessibleInterface*> aSelectedItems; + sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount(); + if (nSelected > std::numeric_limits<int>::max()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::selectedItems: Cell count exceeds maximum int value, " + "using max int."); + nSelected = std::numeric_limits<int>::max(); + } + for (sal_Int64 i = 0; i < nSelected; i++) + { + Reference<XAccessible> xChild = xSelection->getSelectedAccessibleChild(i); + QAccessibleInterface* pInterface + = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xChild)); + aSelectedItems.push_back(pInterface); + } + return aSelectedItems; +} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +QAccessibleInterface* QtAccessibleWidget::selectedItem(int nSelectionIndex) const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return nullptr; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return nullptr; + + if (nSelectionIndex < 0 || nSelectionIndex >= xSelection->getSelectedAccessibleChildCount()) + { + SAL_WARN("vcl.qt", + "QtAccessibleWidget::selectedItem called with invalid index: " << nSelectionIndex); + return nullptr; + } + + Reference<XAccessible> xChild = xSelection->getSelectedAccessibleChild(nSelectionIndex); + if (!xChild) + return nullptr; + + return QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xChild)); +} + +bool QtAccessibleWidget::isSelected(QAccessibleInterface* pItem) const +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return false; + + int nChildIndex = indexOfChild(pItem); + if (nChildIndex < 0) + return false; + + return xSelection->isAccessibleChildSelected(nChildIndex); +} + +bool QtAccessibleWidget::select(QAccessibleInterface* pItem) +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return false; + + int nChildIndex = indexOfChild(pItem); + if (nChildIndex < 0) + return false; + + xSelection->selectAccessibleChild(nChildIndex); + return true; +} + +bool QtAccessibleWidget::unselect(QAccessibleInterface* pItem) +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return false; + + int nChildIndex = indexOfChild(pItem); + if (nChildIndex < 0) + return false; + + xSelection->deselectAccessibleChild(nChildIndex); + return true; +} + +bool QtAccessibleWidget::selectAll() +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return false; + + xSelection->selectAllAccessibleChildren(); + return true; +} + +bool QtAccessibleWidget::clear() +{ + Reference<XAccessibleContext> xAcc = getAccessibleContextImpl(); + if (!xAcc.is()) + return false; + + Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY); + if (!xSelection.is()) + return false; + + xSelection->clearAccessibleSelection(); + return true; +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |