/* -*- 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 <Qt5AccessibleWidget.hxx>
#include <Qt5AccessibleWidget.moc>

#include <QtGui/QAccessibleInterface>

#include <Qt5AccessibleEventListener.hxx>
#include <Qt5Frame.hxx>
#include <Qt5Tools.hxx>
#include <Qt5Widget.hxx>
#include <Qt5XAccessible.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/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/XAccessibleStateSet.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/awt/FontWeight.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>

using namespace css;
using namespace css::accessibility;
using namespace css::beans;
using namespace css::uno;

Qt5AccessibleWidget::Qt5AccessibleWidget(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 Qt5AccessibleEventListener(xAccessible, this));
        xBroadcaster->addAccessibleEventListener(xListener);
    }
}

Reference<XAccessibleContext> Qt5AccessibleWidget::getAccessibleContextImpl() const
{
    Reference<XAccessibleContext> xAc;

    if (m_xAccessible.is())
    {
        try
        {
            xAc = m_xAccessible->getAccessibleContext();
        }
        catch (css::lang::DisposedException /*ex*/)
        {
            SAL_WARN("vcl.qt5", "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.qt5", "Accessible context no longer alive");
        }
    }

    return xAc;
}

QWindow* Qt5AccessibleWidget::window() const { return nullptr; }

int Qt5AccessibleWidget::childCount() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return 0;

    return xAc->getAccessibleChildCount();
}

int Qt5AccessibleWidget::indexOfChild(const QAccessibleInterface* /* child */) const { return 0; }

namespace
{
QAccessible::Relation lcl_matchUnoRelation(short relationType)
{
    switch (relationType)
    {
        case AccessibleRelationType::CONTROLLER_FOR:
            return QAccessible::Controller;
        case AccessibleRelationType::CONTROLLED_BY:
            return QAccessible::Controlled;
        case AccessibleRelationType::LABEL_FOR:
            return QAccessible::Label;
        case AccessibleRelationType::LABELED_BY:
            return QAccessible::Labelled;
        case AccessibleRelationType::INVALID:
        case AccessibleRelationType::CONTENT_FLOWS_FROM:
        case AccessibleRelationType::CONTENT_FLOWS_TO:
        case AccessibleRelationType::MEMBER_OF:
        case AccessibleRelationType::SUB_WINDOW_OF:
        case AccessibleRelationType::NODE_CHILD_OF:
        case AccessibleRelationType::DESCRIBED_BY:
        default:
            SAL_WARN("vcl.qt5", "Unmatched relation: " << relationType);
            return {};
    }
}

short lcl_matchQtRelation(QAccessible::Relation relationType)
{
    switch (relationType)
    {
        case QAccessible::Controller:
            return AccessibleRelationType::CONTROLLER_FOR;
        case QAccessible::Controlled:
            return AccessibleRelationType::CONTROLLED_BY;
        case QAccessible::Label:
            return AccessibleRelationType::LABEL_FOR;
        case QAccessible::Labelled:
            return AccessibleRelationType::LABELED_BY;
        default:
            SAL_WARN("vcl.qt5", "Unmatched relation: " << relationType);
    }
    return 0;
}

void lcl_appendRelation(QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>* relations,
                        AccessibleRelation aRelation)
{
    QAccessible::Relation aQRelation = lcl_matchUnoRelation(aRelation.RelationType);
    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(new Qt5XAccessible(xAccessible)), aQRelation });
    }
}
}

QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>
Qt5AccessibleWidget::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())
    {
        if (match == QAccessible::AllRelations)
        {
            int count = xRelationSet->getRelationCount();
            for (int i = 0; i < count; i++)
            {
                AccessibleRelation aRelation = xRelationSet->getRelation(i);
                lcl_appendRelation(&relations, aRelation);
            }
        }
        else
        {
            AccessibleRelation aRelation = xRelationSet->getRelation(lcl_matchQtRelation(match));
            lcl_appendRelation(&relations, aRelation);
        }
    }

    return relations;
}

QAccessibleInterface* Qt5AccessibleWidget::focusChild() const
{
    /* if (m_pWindow->HasChildPathFocus())
        return QAccessible::queryAccessibleInterface(
            new Qt5XAccessible(m_xAccessible->getAccessibleContext()->getAccessibleChild(index))); */
    return QAccessible::queryAccessibleInterface(object());
}

QRect Qt5AccessibleWidget::rect() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return QRect();

    Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
    awt::Point aPoint = xAccessibleComponent->getLocation();
    awt::Size aSize = xAccessibleComponent->getSize();

    return QRect(aPoint.X, aPoint.Y, aSize.Width, aSize.Height);
}

QAccessibleInterface* Qt5AccessibleWidget::parent() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return nullptr;

    return QAccessible::queryAccessibleInterface(new Qt5XAccessible(xAc->getAccessibleParent()));
}
QAccessibleInterface* Qt5AccessibleWidget::child(int index) const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return nullptr;

    return QAccessible::queryAccessibleInterface(
        new Qt5XAccessible(xAc->getAccessibleChild(index)));
}

QString Qt5AccessibleWidget::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 Qt5AccessibleWidget::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::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::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:
            return QAccessible::Paragraph;

        case AccessibleRole::PASSWORD_TEXT:
            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::Button;

        case AccessibleRole::BUTTON_MENU:
            return QAccessible::Button;

        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;

        /* Ignore window objects for sub-menus, combo- and list boxes,
         *  which are exposed as children of their parents.
         */
        case AccessibleRole::WINDOW: // top-level window without title bar
        {
            return QAccessible::Window;
        }
    }

    SAL_WARN("vcl.qt5",
             "Unmapped role: " << m_xAccessible->getAccessibleContext()->getAccessibleRole());
    return QAccessible::NoRole;
}

namespace
{
void lcl_addState(QAccessible::State* state, sal_Int16 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::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::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:
            // No match
            break;
        case AccessibleStateType::MANAGES_DESCENDANTS:
            // No match
            break;
        case AccessibleStateType::MODAL:
            state->modal = 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.qt5", "Unmapped state: " << nState);
            break;
    }
}
}

QAccessible::State Qt5AccessibleWidget::state() const
{
    QAccessible::State state;

    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return state;

    Reference<XAccessibleStateSet> xStateSet(xAc->getAccessibleStateSet());

    if (!xStateSet.is())
        return state;

    Sequence<sal_Int16> aStates = xStateSet->getStates();

    for (sal_Int32 n = 0; n < aStates.getLength(); n++)
    {
        lcl_addState(&state, n);
    }

    return state;
}

QColor Qt5AccessibleWidget::foregroundColor() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return QColor();

    Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
    return toQColor(xAccessibleComponent->getForeground());
}

QColor Qt5AccessibleWidget::backgroundColor() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return QColor();

    Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
    return toQColor(xAccessibleComponent->getBackground());
}

void* Qt5AccessibleWidget::interface_cast(QAccessible::InterfaceType t)
{
    if (t == QAccessible::ActionInterface)
        return static_cast<QAccessibleActionInterface*>(this);
    if (t == QAccessible::TextInterface)
        return static_cast<QAccessibleTextInterface*>(this);
    if (t == QAccessible::EditableTextInterface)
        return static_cast<QAccessibleEditableTextInterface*>(this);
    if (t == QAccessible::ValueInterface)
        return static_cast<QAccessibleValueInterface*>(this);
    if (t == QAccessible::TableInterface)
        return static_cast<QAccessibleTableInterface*>(this);
    return nullptr;
}

bool Qt5AccessibleWidget::isValid() const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    return xAc.is();
}

QObject* Qt5AccessibleWidget::object() const { return m_pObject; }

void Qt5AccessibleWidget::setText(QAccessible::Text /* t */, const QString& /* text */) {}

QAccessibleInterface* Qt5AccessibleWidget::childAt(int x, int y) const
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return nullptr;

    Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
    return QAccessible::queryAccessibleInterface(
        new Qt5XAccessible(xAccessibleComponent->getAccessibleAtPoint(awt::Point(x, y))));
}

QAccessibleInterface* Qt5AccessibleWidget::customFactory(const QString& classname, QObject* object)
{
    if (classname == QLatin1String("Qt5Widget") && object && object->isWidgetType())
    {
        Qt5Widget* pWidget = static_cast<Qt5Widget*>(object);
        vcl::Window* pWindow = pWidget->frame().GetWindow();

        if (pWindow)
            return new Qt5AccessibleWidget(pWindow->GetAccessible(), object);
    }
    if (classname == QLatin1String("Qt5XAccessible") && object)
    {
        Qt5XAccessible* pXAccessible = dynamic_cast<Qt5XAccessible*>(object);
        if (pXAccessible && pXAccessible->m_xAccessible.is())
            return new Qt5AccessibleWidget(pXAccessible->m_xAccessible, object);
    }

    return nullptr;
}

// QAccessibleActionInterface
QStringList Qt5AccessibleWidget::actionNames() const
{
    QStringList actionNames;
    Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, 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 Qt5AccessibleWidget::doAction(const QString& actionName)
{
    Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, UNO_QUERY);
    if (!xAccessibleAction.is())
        return;

    int index = actionNames().indexOf(actionName);
    if (index == -1)
        return;
    xAccessibleAction->doAccessibleAction(index);
}

QStringList Qt5AccessibleWidget::keyBindingsForAction(const QString& actionName) const
{
    QStringList keyBindings;
    Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, 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;
}

QAccessibleValueInterface* Qt5AccessibleWidget::valueInterface() { return nullptr; }

QAccessibleTextInterface* Qt5AccessibleWidget::textInterface() { return nullptr; }

// QAccessibleTextInterface
void Qt5AccessibleWidget::addSelection(int /* startOffset */, int /* endOffset */)
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::addSelection");
}

namespace
{
OUString lcl_convertFontWeight(double fontWeight)
{
    if (fontWeight == awt::FontWeight::THIN || fontWeight == awt::FontWeight::ULTRALIGHT)
        return "100";
    if (fontWeight == awt::FontWeight::LIGHT)
        return "200";
    if (fontWeight == awt::FontWeight::SEMILIGHT)
        return "300";
    if (fontWeight == awt::FontWeight::NORMAL)
        return "normal";
    if (fontWeight == awt::FontWeight::SEMIBOLD)
        return "500";
    if (fontWeight == awt::FontWeight::BOLD)
        return "bold";
    if (fontWeight == awt::FontWeight::ULTRABOLD)
        return "800";
    if (fontWeight == awt::FontWeight::BLACK)
        return "900";

    // awt::FontWeight::DONTKNOW || fontWeight == awt::FontWeight::NORMAL
    return "normal";
}
}

QString Qt5AccessibleWidget::attributes(int offset, int* startOffset, int* endOffset) const
{
    Reference<XAccessibleText> xText(m_xAccessible, 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(); // currently always returns 0

    const int nTextLength = characterCount();
    if (offset == -1 || offset == nTextLength)
        offset = nTextLength - 1;

    if (offset < 0 || offset > nTextLength)
    {
        *startOffset = -1;
        *endOffset = -1;
        return QString();
    }

    const Sequence<PropertyValue> attribs
        = xText->getCharacterAttributes(offset, Sequence<OUString>());
    OUString aRet;
    for (PropertyValue const& prop : attribs)
    {
        if (prop.Name == "CharFontName")
        {
            aRet += "font-family:" + *o3tl::doAccess<OUString>(prop.Value) + ";";
            continue;
        }
        if (prop.Name == "CharHeight")
        {
            aRet += "font-size:" + OUString::number(*o3tl::doAccess<double>(prop.Value)) + "pt;";
            continue;
        }
        if (prop.Name == "CharWeight")
        {
            aRet += "font-weight:" + lcl_convertFontWeight(*o3tl::doAccess<double>(prop.Value))
                    + ";";
            continue;
        }
    }
    *startOffset = offset;
    *endOffset = offset + 1;
    return toQString(aRet);
}
int Qt5AccessibleWidget::characterCount() const
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is())
        return xText->getCharacterCount();
    return 0;
}
QRect Qt5AccessibleWidget::characterRect(int /* offset */) const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::characterRect");
    return QRect();
}
int Qt5AccessibleWidget::cursorPosition() const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::cursorPosition");
    return 0;
}
int Qt5AccessibleWidget::offsetAtPoint(const QPoint& /* point */) const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::offsetAtPoint");
    return 0;
}
void Qt5AccessibleWidget::removeSelection(int /* selectionIndex */)
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::removeSelection");
}
void Qt5AccessibleWidget::scrollToSubstring(int startIndex, int endIndex)
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is())
        xText->scrollSubstringTo(startIndex, endIndex, AccessibleScrollType_SCROLL_ANYWHERE);
}

void Qt5AccessibleWidget::selection(int selectionIndex, int* startOffset, int* endOffset) const
{
    if (!startOffset && !endOffset)
        return;

    Reference<XAccessibleText> xText;
    if (selectionIndex == 0)
        xText = Reference<XAccessibleText>(m_xAccessible, UNO_QUERY);

    if (startOffset)
        *startOffset = xText.is() ? xText->getSelectionStart() : 0;
    if (endOffset)
        *endOffset = xText.is() ? xText->getSelectionEnd() : 0;
}

int Qt5AccessibleWidget::selectionCount() const
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is() && !xText->getSelectedText().isEmpty())
        return 1; // Only 1 selection supported atm
    return 0;
}
void Qt5AccessibleWidget::setCursorPosition(int position)
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is())
        xText->setCaretPosition(position);
}
void Qt5AccessibleWidget::setSelection(int /* selectionIndex */, int startOffset, int endOffset)
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is())
        xText->setSelection(startOffset, endOffset);
}
QString Qt5AccessibleWidget::text(int startOffset, int endOffset) const
{
    Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
    if (xText.is())
        return toQString(xText->getTextRange(startOffset, endOffset));
    return QString();
}
QString Qt5AccessibleWidget::textAfterOffset(int /* offset */,
                                             QAccessible::TextBoundaryType /* boundaryType */,
                                             int* /* startOffset */, int* /* endOffset */) const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textAfterOffset");
    return QString();
}
QString Qt5AccessibleWidget::textAtOffset(int /* offset */,
                                          QAccessible::TextBoundaryType /* boundaryType */,
                                          int* /* startOffset */, int* /* endOffset */) const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textAtOffset");
    return QString();
}
QString Qt5AccessibleWidget::textBeforeOffset(int /* offset */,
                                              QAccessible::TextBoundaryType /* boundaryType */,
                                              int* /* startOffset */, int* /* endOffset */) const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textBeforeOffset");
    return QString();
}

// QAccessibleEditableTextInterface

void Qt5AccessibleWidget::deleteText(int startOffset, int endOffset)
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return;

    Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
    if (!xEditableText.is())
        return;
    xEditableText->deleteText(startOffset, endOffset);
}

void Qt5AccessibleWidget::insertText(int offset, const QString& text)
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return;

    Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
    if (!xEditableText.is())
        return;
    xEditableText->insertText(toOUString(text), offset);
}

void Qt5AccessibleWidget::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;
    xEditableText->replaceText(startOffset, endOffset, toOUString(text));
}

// QAccessibleValueInterface
QVariant Qt5AccessibleWidget::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 Qt5AccessibleWidget::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 Qt5AccessibleWidget::minimumStepSize() const { return QVariant(); }
QVariant Qt5AccessibleWidget::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 Qt5AccessibleWidget::setCurrentValue(const QVariant& value)
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return;

    Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
    if (!xValue.is())
        return;
    xValue->setCurrentValue(Any(value.toDouble()));
}

// QAccessibleTable
QAccessibleInterface* Qt5AccessibleWidget::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(
        new Qt5XAccessible(xTable->getAccessibleCaption()));
}

QAccessibleInterface* Qt5AccessibleWidget::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;
    return QAccessible::queryAccessibleInterface(
        new Qt5XAccessible(xTable->getAccessibleCellAt(row, column)));
}

int Qt5AccessibleWidget::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 Qt5AccessibleWidget::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 Qt5AccessibleWidget::isColumnSelected(int /* column */) const { return true; }

bool Qt5AccessibleWidget::isRowSelected(int /* row */) const { return true; }

void Qt5AccessibleWidget::modelChange(QAccessibleTableModelChangeEvent*) {}

int Qt5AccessibleWidget::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 Qt5AccessibleWidget::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 Qt5AccessibleWidget::selectColumn(int column)
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return false;

    Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
    if (!xTableSelection.is())
        return false;
    return xTableSelection->selectColumn(column);
}

bool Qt5AccessibleWidget::selectRow(int row)
{
    Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
    if (!xAc.is())
        return false;

    Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
    if (!xTableSelection.is())
        return false;
    return xTableSelection->selectRow(row);
}

int Qt5AccessibleWidget::selectedCellCount() const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTableInterface::selectedCellCount");
    return 0;
}

QList<QAccessibleInterface*> Qt5AccessibleWidget::selectedCells() const
{
    SAL_INFO("vcl.qt5", "Unsupported QAccessibleTableInterface::selectedCells");
    return QList<QAccessibleInterface*>();
}

int Qt5AccessibleWidget::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> Qt5AccessibleWidget::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 Qt5AccessibleWidget::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> Qt5AccessibleWidget::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* Qt5AccessibleWidget::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(
        new Qt5XAccessible(xTable->getAccessibleSummary()));
}

bool Qt5AccessibleWidget::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 Qt5AccessibleWidget::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);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
