/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qaccessiblewidgets.h" #include "qabstracttextdocumentlayout.h" #include "qapplication.h" #include "qclipboard.h" #include "qtextedit.h" #include "private/qtextedit_p.h" #include "qtextdocument.h" #include "qtextobject.h" #include "qscrollbar.h" #include "qdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_ACCESSIBILITY QT_BEGIN_NAMESPACE using namespace QAccessible2; QList childWidgets(const QWidget *widget, bool includeTopLevel) { if (widget == 0) return QList(); QList list = widget->children(); QList widgets; for (int i = 0; i < list.size(); ++i) { QWidget *w = qobject_cast(list.at(i)); if (!w) continue; QString objectName = w->objectName(); if ((includeTopLevel || !w->isWindow()) && !qobject_cast(w) && !qobject_cast(w) && objectName != QLatin1String("qt_rubberband") && objectName != QLatin1String("qt_qmainwindow_extended_splitter")) { widgets.append(w); } } return widgets; } static inline int distance(QWidget *source, QWidget *target, QAccessible::RelationFlag relation) { if (!source || !target) return -1; int returnValue = -1; switch (relation) { case QAccessible::Up: if (target->y() <= source->y()) returnValue = source->y() - target->y(); break; case QAccessible::Down: if (target->y() >= source->y() + source->height()) returnValue = target->y() - (source->y() + source->height()); break; case QAccessible::Right: if (target->x() >= source->x() + source->width()) returnValue = target->x() - (source->x() + source->width()); break; case QAccessible::Left: if (target->x() <= source->x()) returnValue = source->x() - target->x(); break; default: break; } return returnValue; } static inline QWidget *mdiAreaNavigate(QWidget *area, QAccessible::RelationFlag relation, int entry) { #if defined(QT_NO_MDIAREA) && defined(QT_NO_WORKSPACE) Q_UNUSED(area); #endif #ifndef QT_NO_MDIAREA const QMdiArea *mdiArea = qobject_cast(area); #endif #ifndef QT_NO_WORKSPACE const QWorkspace *workspace = qobject_cast(area); #endif if (true #ifndef QT_NO_MDIAREA && !mdiArea #endif #ifndef QT_NO_WORKSPACE && !workspace #endif ) return 0; QWidgetList windows; #ifndef QT_NO_MDIAREA if (mdiArea) { foreach (QMdiSubWindow *window, mdiArea->subWindowList()) windows.append(window); } else #endif { #ifndef QT_NO_WORKSPACE foreach (QWidget *window, workspace->windowList()) windows.append(window->parentWidget()); #endif } if (windows.isEmpty() || entry < 1 || entry > windows.count()) return 0; QWidget *source = windows.at(entry - 1); QMap candidates; foreach (QWidget *window, windows) { if (source == window) continue; int candidateDistance = distance(source, window, relation); if (candidateDistance >= 0) candidates.insert(candidateDistance, window); } int minimumDistance = INT_MAX; QWidget *target = 0; foreach (QWidget *candidate, candidates) { switch (relation) { case QAccessible::Up: case QAccessible::Down: if (qAbs(candidate->x() - source->x()) < minimumDistance) { target = candidate; minimumDistance = qAbs(candidate->x() - source->x()); } break; case QAccessible::Left: case QAccessible::Right: if (qAbs(candidate->y() - source->y()) < minimumDistance) { target = candidate; minimumDistance = qAbs(candidate->y() - source->y()); } break; default: break; } if (minimumDistance == 0) break; } #ifndef QT_NO_WORKSPACE if (workspace) { foreach (QWidget *widget, workspace->windowList()) { if (widget->parentWidget() == target) target = widget; } } #endif return target; } #ifndef QT_NO_TEXTEDIT /*! \class QAccessibleTextEdit \brief The QAccessibleTextEdit class implements the QAccessibleInterface for richtext editors. \internal */ static QTextBlock qTextBlockAt(const QTextDocument *doc, int pos) { Q_ASSERT(pos >= 0); QTextBlock block = doc->begin(); int i = 0; while (block.isValid() && i < pos) { block = block.next(); ++i; } return block; } static int qTextBlockPosition(QTextBlock block) { int child = 0; while (block.isValid()) { block = block.previous(); ++child; } return child; } /*! \fn QAccessibleTextEdit::QAccessibleTextEdit(QWidget* widget) Constructs a QAccessibleTextEdit object for a \a widget. */ QAccessibleTextEdit::QAccessibleTextEdit(QWidget *o) : QAccessibleWidgetEx(o, EditableText) { Q_ASSERT(widget()->inherits("QTextEdit")); childOffset = QAccessibleWidgetEx::childCount(); } /*! Returns the text edit. */ QTextEdit *QAccessibleTextEdit::textEdit() const { return static_cast(widget()); } QRect QAccessibleTextEdit::rect(int child) const { if (child <= childOffset) return QAccessibleWidgetEx::rect(child); QTextEdit *edit = textEdit(); QTextBlock block = qTextBlockAt(edit->document(), child - childOffset - 1); if (!block.isValid()) return QRect(); QRect rect = edit->document()->documentLayout()->blockBoundingRect(block).toRect(); rect.translate(-edit->horizontalScrollBar()->value(), -edit->verticalScrollBar()->value()); rect = edit->viewport()->rect().intersect(rect); if (rect.isEmpty()) return QRect(); return rect.translated(edit->viewport()->mapToGlobal(QPoint(0, 0))); } int QAccessibleTextEdit::childAt(int x, int y) const { QTextEdit *edit = textEdit(); if (!edit->isVisible()) return -1; QPoint point = edit->viewport()->mapFromGlobal(QPoint(x, y)); QTextBlock block = edit->cursorForPosition(point).block(); if (block.isValid()) return qTextBlockPosition(block) + childOffset; return QAccessibleWidgetEx::childAt(x, y); } /*! \reimp */ QString QAccessibleTextEdit::text(Text t, int child) const { if (t == Value) { if (child > childOffset) return qTextBlockAt(textEdit()->document(), child - childOffset - 1).text(); if (!child) return textEdit()->toPlainText(); } return QAccessibleWidgetEx::text(t, child); } /*! \reimp */ void QAccessibleTextEdit::setText(Text t, int child, const QString &text) { if (t != Value || (child > 0 && child <= childOffset)) { QAccessibleWidgetEx::setText(t, child, text); return; } if (textEdit()->isReadOnly()) return; if (!child) { textEdit()->setText(text); return; } QTextBlock block = qTextBlockAt(textEdit()->document(), child - childOffset - 1); if (!block.isValid()) return; QTextCursor cursor(block); cursor.select(QTextCursor::BlockUnderCursor); cursor.insertText(text); } /*! \reimp */ QAccessible::Role QAccessibleTextEdit::role(int child) const { if (child > childOffset) return EditableText; return QAccessibleWidgetEx::role(child); } QVariant QAccessibleTextEdit::invokeMethodEx(QAccessible::Method method, int child, const QVariantList ¶ms) { if (child) return QVariant(); switch (method) { case ListSupportedMethods: { QSet set; set << ListSupportedMethods << SetCursorPosition << GetCursorPosition; return QVariant::fromValue(set | qvariant_cast >( QAccessibleWidgetEx::invokeMethodEx(method, child, params))); } case SetCursorPosition: setCursorPosition(params.value(0).toInt()); return true; case GetCursorPosition: return textEdit()->textCursor().position(); default: return QAccessibleWidgetEx::invokeMethodEx(method, child, params); } } int QAccessibleTextEdit::childCount() const { return childOffset + textEdit()->document()->blockCount(); } #endif // QT_NO_TEXTEDIT #ifndef QT_NO_STACKEDWIDGET // ======================= QAccessibleStackedWidget ====================== QAccessibleStackedWidget::QAccessibleStackedWidget(QWidget *widget) : QAccessibleWidgetEx(widget, LayeredPane) { Q_ASSERT(qobject_cast(widget)); } QVariant QAccessibleStackedWidget::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleStackedWidget::childAt(int x, int y) const { if (!stackedWidget()->isVisible()) return -1; QWidget *currentWidget = stackedWidget()->currentWidget(); if (!currentWidget) return -1; QPoint position = currentWidget->mapFromGlobal(QPoint(x, y)); if (currentWidget->rect().contains(position)) return 1; return -1; } int QAccessibleStackedWidget::childCount() const { return stackedWidget()->count(); } int QAccessibleStackedWidget::indexOfChild(const QAccessibleInterface *child) const { if (!child) return -1; QWidget* widget = qobject_cast(child->object()); int index = stackedWidget()->indexOf(widget); if (index >= 0) // one based counting of children return index + 1; return -1; } int QAccessibleStackedWidget::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; QObject *targetObject = 0; switch (relation) { case Child: if (entry < 1 || entry > stackedWidget()->count()) return -1; targetObject = stackedWidget()->widget(entry-1); break; default: return QAccessibleWidgetEx::navigate(relation, entry, target); } *target = QAccessible::queryAccessibleInterface(targetObject); return *target ? 0 : -1; } QStackedWidget *QAccessibleStackedWidget::stackedWidget() const { return static_cast(object()); } #endif // QT_NO_STACKEDWIDGET #ifndef QT_NO_TOOLBOX // ======================= QAccessibleToolBox ====================== QAccessibleToolBox::QAccessibleToolBox(QWidget *widget) : QAccessibleWidgetEx(widget, LayeredPane) { Q_ASSERT(qobject_cast(widget)); } QString QAccessibleToolBox::text(Text textType, int child) const { if (textType != Value || child <= 0 || child > toolBox()->count()) return QAccessibleWidgetEx::text(textType, child); return toolBox()->itemText(child - 1); } void QAccessibleToolBox::setText(Text textType, int child, const QString &text) { if (textType != Value || child <= 0 || child > toolBox()->count()) { QAccessibleWidgetEx::setText(textType, child, text); return; } toolBox()->setItemText(child - 1, text); } QAccessible::State QAccessibleToolBox::state(int child) const { QWidget *childWidget = toolBox()->widget(child - 1); if (!childWidget) return QAccessibleWidgetEx::state(child); QAccessible::State childState = QAccessible::Normal; if (toolBox()->currentWidget() == childWidget) childState |= QAccessible::Expanded; else childState |= QAccessible::Collapsed; return childState; } QVariant QAccessibleToolBox::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleToolBox::childCount() const { return toolBox()->count(); } int QAccessibleToolBox::indexOfChild(const QAccessibleInterface *child) const { if (!child) return -1; QWidget *childWidget = qobject_cast(child->object()); if (!childWidget) return -1; int index = toolBox()->indexOf(childWidget); if (index != -1) ++index; return index; } int QAccessibleToolBox::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; if (entry <= 0 || entry > toolBox()->count()) return QAccessibleWidgetEx::navigate(relation, entry, target); int index = -1; if (relation == QAccessible::Up) index = entry - 2; else if (relation == QAccessible::Down) index = entry; *target = QAccessible::queryAccessibleInterface(toolBox()->widget(index)); return *target ? 0: -1; } QToolBox * QAccessibleToolBox::toolBox() const { return static_cast(object()); } #endif // QT_NO_TOOLBOX // ======================= QAccessibleMdiArea ====================== #ifndef QT_NO_MDIAREA QAccessibleMdiArea::QAccessibleMdiArea(QWidget *widget) : QAccessibleWidgetEx(widget, LayeredPane) { Q_ASSERT(qobject_cast(widget)); } QAccessible::State QAccessibleMdiArea::state(int child) const { if (child < 0) return QAccessibleWidgetEx::state(child); if (child == 0) return QAccessible::Normal; QList subWindows = mdiArea()->subWindowList(); if (subWindows.isEmpty() || child > subWindows.count()) return QAccessibleWidgetEx::state(child); if (subWindows.at(child - 1) == mdiArea()->activeSubWindow()) return QAccessible::Focused; return QAccessible::Normal; } QVariant QAccessibleMdiArea::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleMdiArea::childCount() const { return mdiArea()->subWindowList().count(); } int QAccessibleMdiArea::indexOfChild(const QAccessibleInterface *child) const { if (!child || !child->object() || mdiArea()->subWindowList().isEmpty()) return -1; if (QMdiSubWindow *window = qobject_cast(child->object())) { int index = mdiArea()->subWindowList().indexOf(window); if (index != -1) return ++index; } return -1; } int QAccessibleMdiArea::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; QWidget *targetObject = 0; QList subWindows = mdiArea()->subWindowList(); switch (relation) { case Child: if (entry < 1 || subWindows.isEmpty() || entry > subWindows.count()) return -1; targetObject = subWindows.at(entry - 1); break; case Up: case Down: case Left: case Right: targetObject = mdiAreaNavigate(mdiArea(), relation, entry); break; default: return QAccessibleWidgetEx::navigate(relation, entry, target); } *target = QAccessible::queryAccessibleInterface(targetObject); return *target ? 0: -1; } QMdiArea *QAccessibleMdiArea::mdiArea() const { return static_cast(object()); } // ======================= QAccessibleMdiSubWindow ====================== QAccessibleMdiSubWindow::QAccessibleMdiSubWindow(QWidget *widget) : QAccessibleWidgetEx(widget, QAccessible::Window) { Q_ASSERT(qobject_cast(widget)); } QString QAccessibleMdiSubWindow::text(Text textType, int child) const { if (textType == QAccessible::Name && (child == 0 || child == 1)) { QString title = mdiSubWindow()->windowTitle(); title.replace(QLatin1String("[*]"), QLatin1String("")); return title; } return QAccessibleWidgetEx::text(textType, child); } void QAccessibleMdiSubWindow::setText(Text textType, int child, const QString &text) { if (textType == QAccessible::Name && (child == 0 || child == 1)) mdiSubWindow()->setWindowTitle(text); else QAccessibleWidgetEx::setText(textType, child, text); } QAccessible::State QAccessibleMdiSubWindow::state(int child) const { if (child != 0 || !mdiSubWindow()->parent()) return QAccessibleWidgetEx::state(child); QAccessible::State state = QAccessible::Normal | QAccessible::Focusable; if (!mdiSubWindow()->isMaximized()) state |= (QAccessible::Movable | QAccessible::Sizeable); if (mdiSubWindow()->isAncestorOf(QApplication::focusWidget()) || QApplication::focusWidget() == mdiSubWindow()) state |= QAccessible::Focused; if (!mdiSubWindow()->isVisible()) state |= QAccessible::Invisible; if (!mdiSubWindow()->parentWidget()->contentsRect().contains(mdiSubWindow()->geometry())) state |= QAccessible::Offscreen; if (!mdiSubWindow()->isEnabled()) state |= QAccessible::Unavailable; return state; } QVariant QAccessibleMdiSubWindow::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleMdiSubWindow::childCount() const { if (mdiSubWindow()->widget()) return 1; return 0; } int QAccessibleMdiSubWindow::indexOfChild(const QAccessibleInterface *child) const { if (child && child->object() && child->object() == mdiSubWindow()->widget()) return 1; return -1; } int QAccessibleMdiSubWindow::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; if (!mdiSubWindow()->parent()) return QAccessibleWidgetEx::navigate(relation, entry, target); QWidget *targetObject = 0; QMdiSubWindow *source = mdiSubWindow(); switch (relation) { case Child: if (entry != 1 || !source->widget()) return -1; targetObject = source->widget(); break; case Up: case Down: case Left: case Right: { if (entry != 0) break; QWidget *parent = source->parentWidget(); while (parent && !parent->inherits("QMdiArea")) parent = parent->parentWidget(); QMdiArea *mdiArea = qobject_cast(parent); if (!mdiArea) break; int index = mdiArea->subWindowList().indexOf(source); if (index == -1) break; if (QWidget *dest = mdiAreaNavigate(mdiArea, relation, index + 1)) { *target = QAccessible::queryAccessibleInterface(dest); return *target ? 0 : -1; } break; } default: return QAccessibleWidgetEx::navigate(relation, entry, target); } *target = QAccessible::queryAccessibleInterface(targetObject); return *target ? 0: -1; } QRect QAccessibleMdiSubWindow::rect(int child) const { if (mdiSubWindow()->isHidden()) return QRect(); if (!mdiSubWindow()->parent()) return QAccessibleWidgetEx::rect(child); const QPoint pos = mdiSubWindow()->mapToGlobal(QPoint(0, 0)); if (child == 0) return QRect(pos, mdiSubWindow()->size()); if (child == 1 && mdiSubWindow()->widget()) { if (mdiSubWindow()->widget()->isHidden()) return QRect(); const QRect contentsRect = mdiSubWindow()->contentsRect(); return QRect(pos.x() + contentsRect.x(), pos.y() + contentsRect.y(), contentsRect.width(), contentsRect.height()); } return QRect(); } int QAccessibleMdiSubWindow::childAt(int x, int y) const { if (!mdiSubWindow()->isVisible()) return -1; if (!mdiSubWindow()->parent()) return QAccessibleWidgetEx::childAt(x, y); const QRect globalGeometry = rect(0); if (!globalGeometry.isValid()) return -1; const QRect globalChildGeometry = rect(1); if (globalChildGeometry.isValid() && globalChildGeometry.contains(QPoint(x, y))) return 1; if (globalGeometry.contains(QPoint(x, y))) return 0; return -1; } QMdiSubWindow *QAccessibleMdiSubWindow::mdiSubWindow() const { return static_cast(object()); } #endif // QT_NO_MDIAREA // ======================= QAccessibleWorkspace ====================== #ifndef QT_NO_WORKSPACE QAccessibleWorkspace::QAccessibleWorkspace(QWidget *widget) : QAccessibleWidgetEx(widget, LayeredPane) { Q_ASSERT(qobject_cast(widget)); } QAccessible::State QAccessibleWorkspace::state(int child) const { if (child < 0) return QAccessibleWidgetEx::state(child); if (child == 0) return QAccessible::Normal; QWidgetList subWindows = workspace()->windowList(); if (subWindows.isEmpty() || child > subWindows.count()) return QAccessibleWidgetEx::state(child); if (subWindows.at(child - 1) == workspace()->activeWindow()) return QAccessible::Focused; return QAccessible::Normal; } QVariant QAccessibleWorkspace::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleWorkspace::childCount() const { return workspace()->windowList().count(); } int QAccessibleWorkspace::indexOfChild(const QAccessibleInterface *child) const { if (!child || !child->object() || workspace()->windowList().isEmpty()) return -1; if (QWidget *window = qobject_cast(child->object())) { int index = workspace()->windowList().indexOf(window); if (index != -1) return ++index; } return -1; } int QAccessibleWorkspace::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; QWidget *targetObject = 0; QWidgetList subWindows = workspace()->windowList(); switch (relation) { case Child: if (entry < 1 || subWindows.isEmpty() || entry > subWindows.count()) return -1; targetObject = subWindows.at(entry - 1); break; case Up: case Down: case Left: case Right: targetObject = mdiAreaNavigate(workspace(), relation, entry); break; default: return QAccessibleWidgetEx::navigate(relation, entry, target); } *target = QAccessible::queryAccessibleInterface(targetObject); return *target ? 0: -1; } QWorkspace *QAccessibleWorkspace::workspace() const { return static_cast(object()); } #endif #ifndef QT_NO_DIALOGBUTTONBOX // ======================= QAccessibleDialogButtonBox ====================== QAccessibleDialogButtonBox::QAccessibleDialogButtonBox(QWidget *widget) : QAccessibleWidgetEx(widget, Grouping) { Q_ASSERT(qobject_cast(widget)); } QVariant QAccessibleDialogButtonBox::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } #endif // QT_NO_DIALOGBUTTONBOX #ifndef QT_NO_TEXTBROWSER QAccessibleTextBrowser::QAccessibleTextBrowser(QWidget *widget) : QAccessibleTextEdit(widget) { Q_ASSERT(qobject_cast(widget)); } QAccessible::Role QAccessibleTextBrowser::role(int child) const { if (child != 0) return QAccessibleTextEdit::role(child); return QAccessible::StaticText; } #endif // QT_NO_TEXTBROWSER #ifndef QT_NO_CALENDARWIDGET // ===================== QAccessibleCalendarWidget ======================== QAccessibleCalendarWidget::QAccessibleCalendarWidget(QWidget *widget) : QAccessibleWidgetEx(widget, Table) { Q_ASSERT(qobject_cast(widget)); } QVariant QAccessibleCalendarWidget::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } int QAccessibleCalendarWidget::childCount() const { return calendarWidget()->isNavigationBarVisible() ? 2 : 1; } int QAccessibleCalendarWidget::indexOfChild(const QAccessibleInterface *child) const { if (!child || !child->object() || childCount() <= 0) return -1; if (qobject_cast(child->object())) return childCount(); return 1; } int QAccessibleCalendarWidget::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { *target = 0; if (entry <= 0 || entry > childCount()) return QAccessibleWidgetEx::navigate(relation, entry, target); QWidget *targetWidget = 0; switch (relation) { case Child: if (childCount() == 1) { targetWidget = calendarView(); } else { if (entry == 1) targetWidget = navigationBar(); else targetWidget = calendarView(); } break; case Up: if (entry == 2) targetWidget = navigationBar(); break; case Down: if (entry == 1 && childCount() == 2) targetWidget = calendarView(); break; default: return QAccessibleWidgetEx::navigate(relation, entry, target); } *target = queryAccessibleInterface(targetWidget); return *target ? 0: -1; } QRect QAccessibleCalendarWidget::rect(int child) const { if (!calendarWidget()->isVisible() || child > childCount()) return QRect(); if (child == 0) return QAccessibleWidgetEx::rect(child); QWidget *childWidget = 0; if (childCount() == 2) childWidget = child == 1 ? navigationBar() : calendarView(); else childWidget = calendarView(); return QRect(childWidget->mapToGlobal(QPoint(0, 0)), childWidget->size()); } int QAccessibleCalendarWidget::childAt(int x, int y) const { const QPoint globalTargetPos = QPoint(x, y); if (!rect(0).contains(globalTargetPos)) return -1; if (rect(1).contains(globalTargetPos)) return 1; if (rect(2).contains(globalTargetPos)) return 2; return 0; } QCalendarWidget *QAccessibleCalendarWidget::calendarWidget() const { return static_cast(object()); } QAbstractItemView *QAccessibleCalendarWidget::calendarView() const { foreach (QObject *child, calendarWidget()->children()) { if (child->objectName() == QLatin1String("qt_calendar_calendarview")) return static_cast(child); } return 0; } QWidget *QAccessibleCalendarWidget::navigationBar() const { foreach (QObject *child, calendarWidget()->children()) { if (child->objectName() == QLatin1String("qt_calendar_navigationbar")) return static_cast(child); } return 0; } #endif // QT_NO_CALENDARWIDGET #ifndef QT_NO_DOCKWIDGET QAccessibleDockWidget::QAccessibleDockWidget(QWidget *widget) : QAccessibleWidgetEx(widget, Window) { } int QAccessibleDockWidget::navigate(RelationFlag relation, int entry, QAccessibleInterface **iface) const { if (relation == Child) { if (entry == 1) { *iface = new QAccessibleTitleBar(dockWidget()); return 0; } else if (entry == 2) { if (dockWidget()->widget()) *iface = QAccessible::queryAccessibleInterface(dockWidget()->widget()); return 0; } *iface = 0; return -1; } return QAccessibleWidgetEx::navigate(relation, entry, iface); } int QAccessibleDockWidget::childAt(int x, int y) const { for (int i = childCount(); i >= 0; --i) { if (rect(i).contains(x,y)) return i; } return -1; } int QAccessibleDockWidget::childCount() const { return dockWidget()->widget() ? 2 : 1; } int QAccessibleDockWidget::indexOfChild(const QAccessibleInterface *child) const { if (child) { if (child->role(0) == TitleBar) { return 1; } else { return 2; //### } } return -1; } QAccessible::Role QAccessibleDockWidget::role(int child) const { switch (child) { case 0: return Window; case 1: return TitleBar; case 2: //### break; default: break; } return NoRole; } QAccessible::State QAccessibleDockWidget::state(int child) const { //### mark tabified widgets as invisible return QAccessibleWidgetEx::state(child); } QRect QAccessibleDockWidget::rect (int child ) const { QRect rect; bool mapToGlobal = true; if (child == 0) { if (dockWidget()->isFloating()) { rect = dockWidget()->frameGeometry(); mapToGlobal = false; } else { rect = dockWidget()->rect(); } }else if (child == 1) { QDockWidgetLayout *layout = qobject_cast(dockWidget()->layout()); rect = layout->titleArea(); }else if (child == 2) { if (dockWidget()->widget()) rect = dockWidget()->widget()->geometry(); } if (rect.isNull()) return rect; if (mapToGlobal) rect.moveTopLeft(dockWidget()->mapToGlobal(rect.topLeft())); return rect; } QVariant QAccessibleDockWidget::invokeMethodEx(QAccessible::Method, int, const QVariantList &) { return QVariant(); } QDockWidget *QAccessibleDockWidget::dockWidget() const { return static_cast(object()); } //// // QAccessibleTitleBar //// QAccessibleTitleBar::QAccessibleTitleBar(QDockWidget *widget) : m_dockWidget(widget) { } int QAccessibleTitleBar::navigate(RelationFlag relation, int entry, QAccessibleInterface **iface) const { if (entry == 0 || relation == Self) { *iface = new QAccessibleTitleBar(dockWidget()); return 0; } switch (relation) { case Child: case FocusChild: if (entry >= 1) { QDockWidgetLayout *layout = dockWidgetLayout(); int index = 1; int role; for (role = QDockWidgetLayout::CloseButton; role <= QDockWidgetLayout::FloatButton; ++role) { QWidget *w = layout->widgetForRole((QDockWidgetLayout::Role)role); if (!w->isVisible()) continue; if (index == entry) break; ++index; } *iface = 0; return role > QDockWidgetLayout::FloatButton ? -1 : index; } break; case Ancestor: { QAccessibleDockWidget *target = new QAccessibleDockWidget(dockWidget()); int index; if (entry == 1) { *iface = target; return 0; } index = target->navigate(Ancestor, entry - 1, iface); delete target; return index; break;} case Sibling: return navigate(Child, entry, iface); break; default: break; } *iface = 0; return -1; } QAccessible::Relation QAccessibleTitleBar::relationTo(int /*child*/, const QAccessibleInterface * /*other*/, int /*otherChild*/) const { return Unrelated; //### } int QAccessibleTitleBar::indexOfChild(const QAccessibleInterface * /*child*/) const { return -1; } int QAccessibleTitleBar::childCount() const { QDockWidgetLayout *layout = dockWidgetLayout(); int count = 0; for (int role = QDockWidgetLayout::CloseButton; role <= QDockWidgetLayout::FloatButton; ++role) { QWidget *w = layout->widgetForRole((QDockWidgetLayout::Role)role); if (w && w->isVisible()) ++count; } return count; } QString QAccessibleTitleBar::text(Text t, int child) const { if (!child) { if (t == Value) { return dockWidget()->windowTitle(); } } return QString(); } QAccessible::State QAccessibleTitleBar::state(int child) const { QAccessible::State state = Normal; if (child) { QDockWidgetLayout *layout = dockWidgetLayout(); QAbstractButton *b = static_cast(layout->widgetForRole((QDockWidgetLayout::Role)child)); if (b) { if (b->isDown()) state |= Pressed; } } else { QDockWidget *w = dockWidget(); if (w->testAttribute(Qt::WA_WState_Visible) == false) state |= Invisible; if (w->focusPolicy() != Qt::NoFocus && w->isActiveWindow()) state |= Focusable; if (w->hasFocus()) state |= Focused; if (!w->isEnabled()) state |= Unavailable; } return state; } QRect QAccessibleTitleBar::rect (int child ) const { bool mapToGlobal = true; QRect rect; if (child == 0) { if (dockWidget()->isFloating()) { rect = dockWidget()->frameGeometry(); QPoint globalPos = dockWidget()->mapToGlobal( dockWidget()->widget()->rect().topLeft() ); globalPos.ry()--; rect.setBottom(globalPos.y()); mapToGlobal = false; } else { QDockWidgetLayout *layout = qobject_cast(dockWidget()->layout()); rect = layout->titleArea(); } }else if (child >= 1 && child <= childCount()) { QDockWidgetLayout *layout = dockWidgetLayout(); int index = 1; for (int role = QDockWidgetLayout::CloseButton; role <= QDockWidgetLayout::FloatButton; ++role) { QWidget *w = layout->widgetForRole((QDockWidgetLayout::Role)role); if (!w || !w->isVisible()) continue; if (index == child) { rect = w->geometry(); break; } ++index; } } if (rect.isNull()) return rect; if (mapToGlobal) rect.moveTopLeft(dockWidget()->mapToGlobal(rect.topLeft())); return rect; } int QAccessibleTitleBar::childAt(int x, int y) const { for (int i = childCount(); i >= 0; --i) { if (rect(i).contains(x,y)) return i; } return -1; } QObject *QAccessibleTitleBar::object() const { return 0; } QDockWidgetLayout *QAccessibleTitleBar::dockWidgetLayout() const { return qobject_cast(dockWidget()->layout()); } QDockWidget *QAccessibleTitleBar::dockWidget() const { return m_dockWidget; } QString QAccessibleTitleBar::actionText(int action, Text t, int child) const { QString str; if (child >= 1 && child <= childCount()) { if (t == Name) { switch (action) { case Press: case DefaultAction: if (child == QDockWidgetLayout::CloseButton) { str = QDockWidget::tr("Close"); } else if (child == QDockWidgetLayout::FloatButton) { str = dockWidget()->isFloating() ? QDockWidget::tr("Dock") : QDockWidget::tr("Float"); } break; default: break; } } } return str; } bool QAccessibleTitleBar::doAction(int action, int child, const QVariantList& /*params*/) { if (!child || !dockWidget()->isEnabled()) return false; switch (action) { case DefaultAction: case Press: { QDockWidgetLayout *layout = dockWidgetLayout(); QAbstractButton *btn = static_cast(layout->widgetForRole((QDockWidgetLayout::Role)child)); if (btn) btn->animateClick(); return true; break;} default: break; } return false; } int QAccessibleTitleBar::userActionCount (int /*child*/) const { return 0; } QAccessible::Role QAccessibleTitleBar::role(int child) const { switch (child) { case 0: return TitleBar; break; default: if (child >= 1 && child <= childCount()) return PushButton; break; } return NoRole; } void QAccessibleTitleBar::setText(Text /*t*/, int /*child*/, const QString &/*text*/) { } bool QAccessibleTitleBar::isValid() const { return dockWidget(); } #endif // QT_NO_DOCKWIDGET #ifndef QT_NO_TEXTEDIT void QAccessibleTextEdit::addSelection(int startOffset, int endOffset) { setSelection(0, startOffset, endOffset); } QString QAccessibleTextEdit::attributes(int offset, int *startOffset, int *endOffset) { // TODO - wait for a definition of attributes Q_UNUSED(offset); Q_UNUSED(startOffset); Q_UNUSED(endOffset); return QString(); } int QAccessibleTextEdit::cursorPosition() { return textEdit()->textCursor().position(); } QRect QAccessibleTextEdit::characterRect(int offset, CoordinateType coordType) { QTextEdit *edit = textEdit(); QTextCursor cursor(edit->document()); cursor.setPosition(offset); if (cursor.position() != offset) return QRect(); QRect r = edit->cursorRect(cursor); if (cursor.movePosition(QTextCursor::NextCharacter)) { r.setWidth(edit->cursorRect(cursor).x() - r.x()); } else { // we don't know the width of the character - maybe because we're at document end // in that case, IAccessible2 tells us to return the width of a default character int averageCharWidth = QFontMetrics(cursor.charFormat().font()).averageCharWidth(); if (edit->layoutDirection() == Qt::RightToLeft) averageCharWidth *= -1; r.setWidth(averageCharWidth); } switch (coordType) { case RelativeToScreen: r.moveTo(edit->viewport()->mapToGlobal(r.topLeft())); break; case RelativeToParent: break; } return r; } int QAccessibleTextEdit::selectionCount() { return textEdit()->textCursor().hasSelection() ? 1 : 0; } int QAccessibleTextEdit::offsetAtPoint(const QPoint &point, CoordinateType coordType) { QTextEdit *edit = textEdit(); QPoint p = point; if (coordType == RelativeToScreen) p = edit->viewport()->mapFromGlobal(p); // convert to document coordinates p += QPoint(edit->horizontalScrollBar()->value(), edit->verticalScrollBar()->value()); return edit->document()->documentLayout()->hitTest(p, Qt::ExactHit); } void QAccessibleTextEdit::selection(int selectionIndex, int *startOffset, int *endOffset) { *startOffset = *endOffset = 0; QTextCursor cursor = textEdit()->textCursor(); if (selectionIndex != 0 || !cursor.hasSelection()) return; *startOffset = cursor.selectionStart(); *endOffset = cursor.selectionEnd(); } QString QAccessibleTextEdit::text(int startOffset, int endOffset) { QTextCursor cursor(textEdit()->document()); cursor.setPosition(startOffset, QTextCursor::MoveAnchor); cursor.setPosition(endOffset, QTextCursor::KeepAnchor); return cursor.selectedText(); } QString QAccessibleTextEdit::textBeforeOffset (int offset, BoundaryType boundaryType, int *startOffset, int *endOffset) { // TODO - what exactly is before? Q_UNUSED(offset); Q_UNUSED(boundaryType); Q_UNUSED(startOffset); Q_UNUSED(endOffset); return QString(); } QString QAccessibleTextEdit::textAfterOffset(int offset, BoundaryType boundaryType, int *startOffset, int *endOffset) { // TODO - what exactly is after? Q_UNUSED(offset); Q_UNUSED(boundaryType); Q_UNUSED(startOffset); Q_UNUSED(endOffset); return QString(); } QString QAccessibleTextEdit::textAtOffset(int offset, BoundaryType boundaryType, int *startOffset, int *endOffset) { Q_ASSERT(startOffset); Q_ASSERT(endOffset); *startOffset = *endOffset = -1; QTextEdit *edit = textEdit(); QTextCursor cursor(edit->document()); if (offset >= characterCount()) return QString(); switch (boundaryType) { case CharBoundary: cursor.setPosition(offset); *startOffset = cursor.position(); cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); *endOffset = cursor.position(); break; case WordBoundary: cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); *startOffset = cursor.position(); cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); *endOffset = cursor.position(); break; case SentenceBoundary: // TODO - what's a sentence? return QString(); case LineBoundary: cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); *startOffset = cursor.position(); cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor); *endOffset = cursor.position(); break; case ParagraphBoundary: cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); *startOffset = cursor.position(); cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); *endOffset = cursor.position(); break; case NoBoundary: { *startOffset = 0; const QString txt = edit->toPlainText(); *endOffset = txt.count(); return txt; } default: qDebug("AccessibleTextAdaptor::textAtOffset: Unknown boundary type %d", boundaryType); return QString(); } return cursor.selectedText(); } void QAccessibleTextEdit::removeSelection(int selectionIndex) { if (selectionIndex != 0) return; QTextCursor cursor = textEdit()->textCursor(); cursor.clearSelection(); textEdit()->setTextCursor(cursor); } void QAccessibleTextEdit::setCursorPosition(int position) { QTextCursor cursor = textEdit()->textCursor(); cursor.setPosition(position); textEdit()->setTextCursor(cursor); } void QAccessibleTextEdit::setSelection(int selectionIndex, int startOffset, int endOffset) { if (selectionIndex != 0) return; QTextCursor cursor = textEdit()->textCursor(); cursor.setPosition(startOffset, QTextCursor::MoveAnchor); cursor.setPosition(endOffset, QTextCursor::KeepAnchor); textEdit()->setTextCursor(cursor); } int QAccessibleTextEdit::characterCount() { return textEdit()->toPlainText().count(); } void QAccessibleTextEdit::scrollToSubstring(int startIndex, int endIndex) { QTextEdit *edit = textEdit(); QTextCursor cursor(edit->document()); cursor.setPosition(startIndex); QRect r = edit->cursorRect(cursor); cursor.setPosition(endIndex); r.setBottomRight(edit->cursorRect(cursor).bottomRight()); r.moveTo(r.x() + edit->horizontalScrollBar()->value(), r.y() + edit->verticalScrollBar()->value()); // E V I L, but ensureVisible is not public if (!QMetaObject::invokeMethod(edit, "_q_ensureVisible", Q_ARG(QRectF, r))) qWarning("AccessibleTextEdit::scrollToSubstring failed!"); } static QTextCursor cursorForRange(QTextEdit *textEdit, int startOffset, int endOffset) { QTextCursor cursor(textEdit->document()); cursor.setPosition(startOffset, QTextCursor::MoveAnchor); cursor.setPosition(endOffset, QTextCursor::KeepAnchor); return cursor; } void QAccessibleTextEdit::copyText(int startOffset, int endOffset) { QTextCursor cursor = cursorForRange(textEdit(), startOffset, endOffset); if (!cursor.hasSelection()) return; // QApplication::clipboard()->setMimeData(new QTextEditMimeData(cursor.selection())); } void QAccessibleTextEdit::deleteText(int startOffset, int endOffset) { QTextCursor cursor = cursorForRange(textEdit(), startOffset, endOffset); cursor.removeSelectedText(); } void QAccessibleTextEdit::insertText(int offset, const QString &text) { QTextCursor cursor(textEdit()->document()); cursor.setPosition(offset); cursor.insertText(text); } void QAccessibleTextEdit::cutText(int startOffset, int endOffset) { QTextCursor cursor = cursorForRange(textEdit(), startOffset, endOffset); if (!cursor.hasSelection()) return; // QApplication::clipboard()->setMimeData(new QTextEditMimeData(cursor.selection())); cursor.removeSelectedText(); } void QAccessibleTextEdit::pasteText(int offset) { QTextEdit *edit = textEdit(); QTextCursor oldCursor = edit->textCursor(); QTextCursor newCursor = oldCursor; newCursor.setPosition(offset); edit->setTextCursor(newCursor); #ifndef QT_NO_CLIPBOARD edit->paste(); #endif edit->setTextCursor(oldCursor); } void QAccessibleTextEdit::replaceText(int startOffset, int endOffset, const QString &text) { QTextCursor cursor = cursorForRange(textEdit(), startOffset, endOffset); cursor.removeSelectedText(); cursor.insertText(text); } void QAccessibleTextEdit::setAttributes(int startOffset, int endOffset, const QString &attributes) { // TODO Q_UNUSED(startOffset); Q_UNUSED(endOffset); Q_UNUSED(attributes); } #endif // QT_NO_TEXTEDIT #ifndef QT_NO_MAINWINDOW QAccessibleMainWindow::QAccessibleMainWindow(QWidget *widget) : QAccessibleWidgetEx(widget, Window) { } QVariant QAccessibleMainWindow::invokeMethodEx(QAccessible::Method /*method*/, int /*child*/, const QVariantList & /*params*/) { return QVariant(); } int QAccessibleMainWindow::childCount() const { QList kids = childWidgets(mainWindow(), true); return kids.count(); } int QAccessibleMainWindow::indexOfChild(const QAccessibleInterface *iface) const { QList kids = childWidgets(mainWindow(), true); int childIndex = kids.indexOf(static_cast(iface->object())); return childIndex == -1 ? -1 : ++childIndex; } int QAccessibleMainWindow::navigate(RelationFlag relation, int entry, QAccessibleInterface **iface) const { if (relation == Child && entry >= 1) { QList kids = childWidgets(mainWindow(), true); if (entry <= kids.count()) { *iface = QAccessible::queryAccessibleInterface(kids.at(entry - 1)); return *iface ? 0 : -1; } } return QAccessibleWidgetEx::navigate(relation, entry, iface); } int QAccessibleMainWindow::childAt(int x, int y) const { QWidget *w = widget(); if (!w->isVisible()) return -1; QPoint gp = w->mapToGlobal(QPoint(0, 0)); if (!QRect(gp.x(), gp.y(), w->width(), w->height()).contains(x, y)) return -1; QWidgetList kids = childWidgets(mainWindow(), true); QPoint rp = mainWindow()->mapFromGlobal(QPoint(x, y)); for (int i = 0; i < kids.size(); ++i) { QWidget *child = kids.at(i); if (!child->isWindow() && !child->isHidden() && child->geometry().contains(rp)) { return i + 1; } } return 0; } QMainWindow *QAccessibleMainWindow::mainWindow() const { return qobject_cast(object()); } #endif //QT_NO_MAINWINDOW QT_END_NAMESPACE #endif // QT_NO_ACCESSIBILITY