/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "simplewidgets_p.h" #if QT_CONFIG(abstractbutton) #include #endif #if QT_CONFIG(checkbox) #include #endif #if QT_CONFIG(pushbutton) #include #endif #if QT_CONFIG(progressbar) #include #endif #if QT_CONFIG(statusbar) #include #endif #if QT_CONFIG(radiobutton) #include #endif #if QT_CONFIG(toolbutton) #include #endif #if QT_CONFIG(menu) #include #endif #if QT_CONFIG(label) #include #endif #if QT_CONFIG(groupbox) #include #endif #if QT_CONFIG(lcdnumber) #include #endif #if QT_CONFIG(lineedit) #include #include #endif #include #include #include #include #include #include #include #ifdef Q_OS_MAC #include #endif QT_BEGIN_NAMESPACE #ifndef QT_NO_ACCESSIBILITY extern QList childWidgets(const QWidget *widget); QString qt_accStripAmp(const QString &text); QString qt_accHotKey(const QString &text); #if QT_CONFIG(abstractbutton) /*! \class QAccessibleButton \brief The QAccessibleButton class implements the QAccessibleInterface for button type widgets. \internal \ingroup accessibility */ /*! Creates a QAccessibleButton object for \a w. */ QAccessibleButton::QAccessibleButton(QWidget *w) : QAccessibleWidget(w) { Q_ASSERT(button()); // FIXME: The checkable state of the button is dynamic, // while we only update the controlling signal once :( if (button()->isCheckable()) addControllingSignal(QLatin1String("toggled(bool)")); else addControllingSignal(QLatin1String("clicked()")); } /*! Returns the button. */ QAbstractButton *QAccessibleButton::button() const { return qobject_cast(object()); } /*! \reimp */ QString QAccessibleButton::text(QAccessible::Text t) const { QString str; switch (t) { case QAccessible::Accelerator: { #if QT_CONFIG(shortcut) && QT_CONFIG(pushbutton) QPushButton *pb = qobject_cast(object()); if (pb && pb->isDefault()) str = QKeySequence(Qt::Key_Enter).toString(QKeySequence::NativeText); #endif if (str.isEmpty()) str = qt_accHotKey(button()->text()); } break; case QAccessible::Name: str = widget()->accessibleName(); if (str.isEmpty()) str = qt_accStripAmp(button()->text()); break; default: break; } if (str.isEmpty()) str = QAccessibleWidget::text(t); return str; } QAccessible::State QAccessibleButton::state() const { QAccessible::State state = QAccessibleWidget::state(); QAbstractButton *b = button(); #if QT_CONFIG(checkbox) QCheckBox *cb = qobject_cast(b); #endif if (b->isCheckable()) state.checkable = true; if (b->isChecked()) state.checked = true; #if QT_CONFIG(checkbox) if (cb && cb->checkState() == Qt::PartiallyChecked) state.checkStateMixed = true; #endif if (b->isDown()) state.pressed = true; #if QT_CONFIG(pushbutton) QPushButton *pb = qobject_cast(b); if (pb) { if (pb->isDefault()) state.defaultButton = true; #if QT_CONFIG(menu) if (pb->menu()) state.hasPopup = true; #endif } #endif return state; } QRect QAccessibleButton::rect() const { QAbstractButton *ab = button(); if (!ab->isVisible()) return QRect(); #if QT_CONFIG(checkbox) if (QCheckBox *cb = qobject_cast(ab)) { QPoint wpos = cb->mapToGlobal(QPoint(0, 0)); QStyleOptionButton opt; cb->initStyleOption(&opt); return cb->style()->subElementRect(QStyle::SE_CheckBoxClickRect, &opt, cb).translated(wpos); } #endif #if QT_CONFIG(radiobutton) else if (QRadioButton *rb = qobject_cast(ab)) { QPoint wpos = rb->mapToGlobal(QPoint(0, 0)); QStyleOptionButton opt; rb->initStyleOption(&opt); return rb->style()->subElementRect(QStyle::SE_RadioButtonClickRect, &opt, rb).translated(wpos); } #endif return QAccessibleWidget::rect(); } QAccessible::Role QAccessibleButton::role() const { QAbstractButton *ab = button(); #if QT_CONFIG(menu) if (QPushButton *pb = qobject_cast(ab)) { if (pb->menu()) return QAccessible::ButtonMenu; } #endif if (ab->isCheckable()) return ab->autoExclusive() ? QAccessible::RadioButton : QAccessible::CheckBox; return QAccessible::Button; } QStringList QAccessibleButton::actionNames() const { QStringList names; if (widget()->isEnabled()) { switch (role()) { case QAccessible::ButtonMenu: names << showMenuAction(); break; case QAccessible::RadioButton: names << toggleAction(); break; default: if (button()->isCheckable()) { names << toggleAction(); } else { names << pressAction(); } break; } } names << QAccessibleWidget::actionNames(); return names; } void QAccessibleButton::doAction(const QString &actionName) { if (!widget()->isEnabled()) return; if (actionName == pressAction() || actionName == showMenuAction()) { #if QT_CONFIG(menu) QPushButton *pb = qobject_cast(object()); if (pb && pb->menu()) pb->showMenu(); else #endif button()->animateClick(); } else if (actionName == toggleAction()) { button()->toggle(); } else { QAccessibleWidget::doAction(actionName); } } QStringList QAccessibleButton::keyBindingsForAction(const QString &actionName) const { if (actionName == pressAction()) { #ifndef QT_NO_SHORTCUT return QStringList() << button()->shortcut().toString(); #endif } return QStringList(); } #endif // QT_CONFIG(abstractbutton) #if QT_CONFIG(toolbutton) /*! \class QAccessibleToolButton \brief The QAccessibleToolButton class implements the QAccessibleInterface for tool buttons. \internal \ingroup accessibility */ /*! Creates a QAccessibleToolButton object for \a w. */ QAccessibleToolButton::QAccessibleToolButton(QWidget *w) : QAccessibleButton(w) { Q_ASSERT(toolButton()); } /*! Returns the button. */ QToolButton *QAccessibleToolButton::toolButton() const { return qobject_cast(object()); } /*! Returns \c true if this tool button is a split button. */ bool QAccessibleToolButton::isSplitButton() const { #if QT_CONFIG(menu) return toolButton()->menu() && toolButton()->popupMode() == QToolButton::MenuButtonPopup; #else return false; #endif } QAccessible::State QAccessibleToolButton::state() const { QAccessible::State st = QAccessibleButton::state(); if (toolButton()->autoRaise()) st.hotTracked = true; #if QT_CONFIG(menu) if (toolButton()->menu()) st.hasPopup = true; #endif return st; } int QAccessibleToolButton::childCount() const { return isSplitButton() ? 1 : 0; } QAccessible::Role QAccessibleToolButton::role() const { #if QT_CONFIG(menu) QAbstractButton *ab = button(); QToolButton *tb = qobject_cast(ab); if (!tb->menu()) return tb->isCheckable() ? QAccessible::CheckBox : QAccessible::PushButton; else if (tb->popupMode() == QToolButton::DelayedPopup) return QAccessible::ButtonDropDown; #endif return QAccessible::ButtonMenu; } QAccessibleInterface *QAccessibleToolButton::child(int index) const { #if QT_CONFIG(menu) if (index == 0 && toolButton()->menu()) { return QAccessible::queryAccessibleInterface(toolButton()->menu()); } #else Q_UNUSED(index) #endif return 0; } /* The three different tool button types can have the following actions: | DelayedPopup | ShowMenuAction + (PressedAction || CheckedAction) | | MenuButtonPopup | ShowMenuAction + (PressedAction || CheckedAction) | | InstantPopup | ShowMenuAction | */ QStringList QAccessibleToolButton::actionNames() const { QStringList names; if (widget()->isEnabled()) { #if QT_CONFIG(menu) if (toolButton()->menu()) names << showMenuAction(); if (toolButton()->popupMode() != QToolButton::InstantPopup) names << QAccessibleButton::actionNames(); #endif } return names; } void QAccessibleToolButton::doAction(const QString &actionName) { if (!widget()->isEnabled()) return; if (actionName == pressAction()) { button()->click(); } else if (actionName == showMenuAction()) { #if QT_CONFIG(menu) if (toolButton()->popupMode() != QToolButton::InstantPopup) { toolButton()->setDown(true); toolButton()->showMenu(); } #endif } else { QAccessibleButton::doAction(actionName); } } #endif // QT_CONFIG(toolbutton) /*! \class QAccessibleDisplay \brief The QAccessibleDisplay class implements the QAccessibleInterface for widgets that display information. \internal \ingroup accessibility */ /*! Constructs a QAccessibleDisplay object for \a w. \a role is propagated to the QAccessibleWidget constructor. */ QAccessibleDisplay::QAccessibleDisplay(QWidget *w, QAccessible::Role role) : QAccessibleWidget(w, role) { } QAccessible::Role QAccessibleDisplay::role() const { #if QT_CONFIG(label) QLabel *l = qobject_cast(object()); if (l) { if (l->pixmap()) return QAccessible::Graphic; #ifndef QT_NO_PICTURE if (l->picture()) return QAccessible::Graphic; #endif #if QT_CONFIG(movie) if (l->movie()) return QAccessible::Animation; #endif #if QT_CONFIG(progressbar) } else if (qobject_cast(object())) { return QAccessible::ProgressBar; #endif #if QT_CONFIG(statusbar) } else if (qobject_cast(object())) { return QAccessible::StatusBar; #endif } #endif return QAccessibleWidget::role(); } QString QAccessibleDisplay::text(QAccessible::Text t) const { QString str; switch (t) { case QAccessible::Name: str = widget()->accessibleName(); if (str.isEmpty()) { if (false) { #if QT_CONFIG(label) } else if (qobject_cast(object())) { QLabel *label = qobject_cast(object()); str = label->text(); #ifndef QT_NO_TEXTHTMLPARSER if (label->textFormat() == Qt::RichText || (label->textFormat() == Qt::AutoText && Qt::mightBeRichText(str))) { QTextDocument doc; doc.setHtml(str); str = doc.toPlainText(); } #endif #ifndef QT_NO_SHORTCUT if (label->buddy()) str = qt_accStripAmp(str); #endif #endif // QT_CONFIG(label) #if QT_CONFIG(lcdnumber) } else if (qobject_cast(object())) { QLCDNumber *l = qobject_cast(object()); if (l->digitCount()) str = QString::number(l->value()); else str = QString::number(l->intValue()); #endif #if QT_CONFIG(statusbar) } else if (qobject_cast(object())) { return qobject_cast(object())->currentMessage(); #endif } } break; case QAccessible::Value: #if QT_CONFIG(progressbar) if (qobject_cast(object())) str = QString::number(qobject_cast(object())->value()); #endif break; default: break; } if (str.isEmpty()) str = QAccessibleWidget::text(t); return str; } /*! \reimp */ QVector > QAccessibleDisplay::relations(QAccessible::Relation match /* = QAccessible::AllRelations */) const { QVector > rels = QAccessibleWidget::relations(match); #if QT_CONFIG(shortcut) && QT_CONFIG(label) if (match & QAccessible::Labelled) { if (QLabel *label = qobject_cast(object())) { const QAccessible::Relation rel = QAccessible::Labelled; if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(label->buddy())) rels.append(qMakePair(iface, rel)); } } #endif return rels; } void *QAccessibleDisplay::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::ImageInterface) return static_cast(this); return QAccessibleWidget::interface_cast(t); } /*! \internal */ QString QAccessibleDisplay::imageDescription() const { #ifndef QT_NO_TOOLTIP return widget()->toolTip(); #else return QString(); #endif } /*! \internal */ QSize QAccessibleDisplay::imageSize() const { #if QT_CONFIG(label) QLabel *label = qobject_cast(widget()); if (!label) #endif return QSize(); #if QT_CONFIG(label) const QPixmap *pixmap = label->pixmap(); if (!pixmap) return QSize(); return pixmap->size(); #endif } /*! \internal */ QPoint QAccessibleDisplay::imagePosition() const { #if QT_CONFIG(label) QLabel *label = qobject_cast(widget()); if (!label) #endif return QPoint(); #if QT_CONFIG(label) const QPixmap *pixmap = label->pixmap(); if (!pixmap) return QPoint(); return QPoint(label->mapToGlobal(label->pos())); #endif } #if QT_CONFIG(groupbox) QAccessibleGroupBox::QAccessibleGroupBox(QWidget *w) : QAccessibleWidget(w) { } QGroupBox* QAccessibleGroupBox::groupBox() const { return static_cast(widget()); } QString QAccessibleGroupBox::text(QAccessible::Text t) const { QString txt = QAccessibleWidget::text(t); if (txt.isEmpty()) { switch (t) { case QAccessible::Name: txt = qt_accStripAmp(groupBox()->title()); break; #if QT_CONFIG(tooltip) case QAccessible::Description: txt = groupBox()->toolTip(); break; #endif case QAccessible::Accelerator: txt = qt_accHotKey(groupBox()->title()); break; default: break; } } return txt; } QAccessible::State QAccessibleGroupBox::state() const { QAccessible::State st = QAccessibleWidget::state(); st.checkable = groupBox()->isCheckable(); st.checked = groupBox()->isChecked(); return st; } QAccessible::Role QAccessibleGroupBox::role() const { return groupBox()->isCheckable() ? QAccessible::CheckBox : QAccessible::Grouping; } QVector > QAccessibleGroupBox::relations(QAccessible::Relation match /* = QAccessible::AllRelations */) const { QVector > rels = QAccessibleWidget::relations(match); if ((match & QAccessible::Labelled) && (!groupBox()->title().isEmpty())) { const QList kids = childWidgets(widget()); for (QWidget *kid : kids) { QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(kid); if (iface) rels.append(qMakePair(iface, QAccessible::Relation(QAccessible::Labelled))); } } return rels; } QStringList QAccessibleGroupBox::actionNames() const { QStringList actions = QAccessibleWidget::actionNames(); if (groupBox()->isCheckable()) { actions.prepend(QAccessibleActionInterface::toggleAction()); } return actions; } void QAccessibleGroupBox::doAction(const QString &actionName) { if (actionName == QAccessibleActionInterface::toggleAction()) groupBox()->setChecked(!groupBox()->isChecked()); } QStringList QAccessibleGroupBox::keyBindingsForAction(const QString &) const { return QStringList(); } #endif #if QT_CONFIG(lineedit) /*! \class QAccessibleLineEdit \brief The QAccessibleLineEdit class implements the QAccessibleInterface for widgets with editable text \internal \ingroup accessibility */ /*! Constructs a QAccessibleLineEdit object for \a w. \a name is propagated to the QAccessibleWidget constructor. */ QAccessibleLineEdit::QAccessibleLineEdit(QWidget *w, const QString &name) : QAccessibleWidget(w, QAccessible::EditableText, name) { addControllingSignal(QLatin1String("textChanged(const QString&)")); addControllingSignal(QLatin1String("returnPressed()")); } /*! Returns the line edit. */ QLineEdit *QAccessibleLineEdit::lineEdit() const { return qobject_cast(object()); } QString QAccessibleLineEdit::text(QAccessible::Text t) const { QString str; switch (t) { case QAccessible::Value: if (lineEdit()->echoMode() == QLineEdit::Normal) str = lineEdit()->text(); else if (lineEdit()->echoMode() != QLineEdit::NoEcho) str = QString(lineEdit()->text().length(), QChar::fromLatin1('*')); break; default: break; } if (str.isEmpty()) str = QAccessibleWidget::text(t); return str; } void QAccessibleLineEdit::setText(QAccessible::Text t, const QString &text) { if (t != QAccessible::Value) { QAccessibleWidget::setText(t, text); return; } QString newText = text; #if QT_CONFIG(validator) if (lineEdit()->validator()) { int pos = 0; if (lineEdit()->validator()->validate(newText, pos) != QValidator::Acceptable) return; } #endif lineEdit()->setText(newText); } QAccessible::State QAccessibleLineEdit::state() const { QAccessible::State state = QAccessibleWidget::state(); QLineEdit *l = lineEdit(); if (l->isReadOnly()) state.readOnly = true; else state.editable = true; if (l->echoMode() != QLineEdit::Normal) state.passwordEdit = true; state.selectableText = true; return state; } void *QAccessibleLineEdit::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TextInterface) return static_cast(this); if (t == QAccessible::EditableTextInterface) return static_cast(this); return QAccessibleWidget::interface_cast(t); } void QAccessibleLineEdit::addSelection(int startOffset, int endOffset) { setSelection(0, startOffset, endOffset); } QString QAccessibleLineEdit::attributes(int offset, int *startOffset, int *endOffset) const { // QLineEdit doesn't have text attributes *startOffset = *endOffset = offset; return QString(); } int QAccessibleLineEdit::cursorPosition() const { return lineEdit()->cursorPosition(); } QRect QAccessibleLineEdit::characterRect(int offset) const { int x = lineEdit()->d_func()->control->cursorToX(offset); int y; lineEdit()->getTextMargins(0, &y, 0, 0); QFontMetrics fm(lineEdit()->font()); const QString ch = text(offset, offset + 1); if (ch.isEmpty()) return QRect(); int w = fm.width(ch); int h = fm.height(); QRect r(x, y, w, h); r.moveTo(lineEdit()->mapToGlobal(r.topLeft())); return r; } int QAccessibleLineEdit::selectionCount() const { return lineEdit()->hasSelectedText() ? 1 : 0; } int QAccessibleLineEdit::offsetAtPoint(const QPoint &point) const { QPoint p = lineEdit()->mapFromGlobal(point); return lineEdit()->cursorPositionAt(p); } void QAccessibleLineEdit::selection(int selectionIndex, int *startOffset, int *endOffset) const { *startOffset = *endOffset = 0; if (selectionIndex != 0) return; *startOffset = lineEdit()->selectionStart(); *endOffset = *startOffset + lineEdit()->selectedText().count(); } QString QAccessibleLineEdit::text(int startOffset, int endOffset) const { if (startOffset > endOffset) return QString(); if (lineEdit()->echoMode() != QLineEdit::Normal) return QString(); return lineEdit()->text().mid(startOffset, endOffset - startOffset); } QString QAccessibleLineEdit::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const { if (lineEdit()->echoMode() != QLineEdit::Normal) { *startOffset = *endOffset = -1; return QString(); } if (offset == -2) offset = cursorPosition(); return QAccessibleTextInterface::textBeforeOffset(offset, boundaryType, startOffset, endOffset); } QString QAccessibleLineEdit::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const { if (lineEdit()->echoMode() != QLineEdit::Normal) { *startOffset = *endOffset = -1; return QString(); } if (offset == -2) offset = cursorPosition(); return QAccessibleTextInterface::textAfterOffset(offset, boundaryType, startOffset, endOffset); } QString QAccessibleLineEdit::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const { if (lineEdit()->echoMode() != QLineEdit::Normal) { *startOffset = *endOffset = -1; return QString(); } if (offset == -2) offset = cursorPosition(); return QAccessibleTextInterface::textAtOffset(offset, boundaryType, startOffset, endOffset); } void QAccessibleLineEdit::removeSelection(int selectionIndex) { if (selectionIndex != 0) return; lineEdit()->deselect(); } void QAccessibleLineEdit::setCursorPosition(int position) { lineEdit()->setCursorPosition(position); } void QAccessibleLineEdit::setSelection(int selectionIndex, int startOffset, int endOffset) { if (selectionIndex != 0) return; lineEdit()->setSelection(startOffset, endOffset - startOffset); } int QAccessibleLineEdit::characterCount() const { return lineEdit()->text().count(); } void QAccessibleLineEdit::scrollToSubstring(int startIndex, int endIndex) { lineEdit()->setCursorPosition(endIndex); lineEdit()->setCursorPosition(startIndex); } void QAccessibleLineEdit::deleteText(int startOffset, int endOffset) { lineEdit()->setText(lineEdit()->text().remove(startOffset, endOffset - startOffset)); } void QAccessibleLineEdit::insertText(int offset, const QString &text) { lineEdit()->setText(lineEdit()->text().insert(offset, text)); } void QAccessibleLineEdit::replaceText(int startOffset, int endOffset, const QString &text) { lineEdit()->setText(lineEdit()->text().replace(startOffset, endOffset - startOffset, text)); } #endif // QT_CONFIG(lineedit) #if QT_CONFIG(progressbar) QAccessibleProgressBar::QAccessibleProgressBar(QWidget *o) : QAccessibleDisplay(o) { Q_ASSERT(progressBar()); } void *QAccessibleProgressBar::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::ValueInterface) return static_cast(this); return QAccessibleDisplay::interface_cast(t); } QVariant QAccessibleProgressBar::currentValue() const { return progressBar()->value(); } QVariant QAccessibleProgressBar::maximumValue() const { return progressBar()->maximum(); } QVariant QAccessibleProgressBar::minimumValue() const { return progressBar()->minimum(); } QVariant QAccessibleProgressBar::minimumStepSize() const { // This is arbitrary since any value between min and max is valid. // Some screen readers (orca use it to calculate how many digits to display though, // so it makes sense to return a "sensible" value. Providing 100 increments seems ok. return (progressBar()->maximum() - progressBar()->minimum()) / 100.0; } QProgressBar *QAccessibleProgressBar::progressBar() const { return qobject_cast(object()); } #endif QAccessibleWindowContainer::QAccessibleWindowContainer(QWidget *w) : QAccessibleWidget(w) { } int QAccessibleWindowContainer::childCount() const { if (container()->containedWindow() && QAccessible::queryAccessibleInterface(container()->containedWindow())) return 1; return 0; } int QAccessibleWindowContainer::indexOfChild(const QAccessibleInterface *child) const { if (child->object() == container()->containedWindow()) return 0; return -1; } QAccessibleInterface *QAccessibleWindowContainer::child(int i) const { if (i == 0) return QAccessible::queryAccessibleInterface(container()->containedWindow()); return 0; } QWindowContainer *QAccessibleWindowContainer::container() const { return static_cast(widget()); } #endif // QT_NO_ACCESSIBILITY QT_END_NAMESPACE