1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "complexwidgets_p.h"
41
42#include <qaccessible.h>
43#include <qapplication.h>
44#include <qevent.h>
45#if QT_CONFIG(itemviews)
46#include <qheaderview.h>
47#endif
48#if QT_CONFIG(tabbar)
49#include <qtabbar.h>
50#include <private/qtabbar_p.h>
51#endif
52#if QT_CONFIG(combobox)
53#include <qcombobox.h>
54#endif
55#if QT_CONFIG(lineedit)
56#include <qlineedit.h>
57#endif
58#include <qstyle.h>
59#include <qstyleoption.h>
60#include <qtooltip.h>
61#if QT_CONFIG(whatsthis)
62#include <qwhatsthis.h>
63#endif
64#include <QAbstractScrollArea>
65#if QT_CONFIG(scrollarea)
66#include <QScrollArea>
67#endif
68#if QT_CONFIG(scrollbar)
69#include <QScrollBar>
70#endif
71#include <QDebug>
72
73#ifndef QT_NO_ACCESSIBILITY
74
75QT_BEGIN_NAMESPACE
76
77QString qt_accStripAmp(const QString &text);
78QString qt_accHotKey(const QString &text);
79
80#if QT_CONFIG(tabbar)
81/*!
82 \class QAccessibleTabBar
83 \brief The QAccessibleTabBar class implements the QAccessibleInterface for tab bars.
84 \internal
85
86 \ingroup accessibility
87*/
88
89class QAccessibleTabButton: public QAccessibleInterface, public QAccessibleActionInterface
90{
91public:
92 QAccessibleTabButton(QTabBar *parent, int index)
93 : m_parent(parent), m_index(index)
94 {}
95
96 void *interface_cast(QAccessible::InterfaceType t) override {
97 if (t == QAccessible::ActionInterface) {
98 return static_cast<QAccessibleActionInterface*>(this);
99 }
100 return nullptr;
101 }
102
103 QObject *object() const override { return nullptr; }
104 QAccessible::Role role() const override { return QAccessible::PageTab; }
105 QAccessible::State state() const override {
106 if (!isValid()) {
107 QAccessible::State s;
108 s.invalid = true;
109 return s;
110 }
111
112 QAccessible::State s = parent()->state();
113 s.focused = (m_index == m_parent->currentIndex());
114 return s;
115 }
116 QRect rect() const override {
117 if (!isValid())
118 return QRect();
119
120 QPoint tp = m_parent->mapToGlobal(QPoint(0,0));
121 QRect rec = m_parent->tabRect(index: m_index);
122 rec = QRect(tp.x() + rec.x(), tp.y() + rec.y(), rec.width(), rec.height());
123 return rec;
124 }
125
126 bool isValid() const override {
127 if (m_parent) {
128 if (static_cast<QWidget *>(m_parent.data())->d_func()->data.in_destructor)
129 return false;
130 return m_parent->count() > m_index;
131 }
132 return false;
133 }
134
135 QAccessibleInterface *childAt(int, int) const override { return nullptr; }
136 int childCount() const override { return 0; }
137 int indexOfChild(const QAccessibleInterface *) const override { return -1; }
138
139 QString text(QAccessible::Text t) const override
140 {
141 if (!isValid())
142 return QString();
143 QString str;
144 switch (t) {
145 case QAccessible::Name:
146 str = m_parent->accessibleTabName(index: m_index);
147 if (str.isEmpty())
148 str = qt_accStripAmp(text: m_parent->tabText(index: m_index));
149 break;
150 case QAccessible::Accelerator:
151 str = qt_accHotKey(text: m_parent->tabText(index: m_index));
152 break;
153#if QT_CONFIG(tooltip)
154 case QAccessible::Description:
155 str = m_parent->tabToolTip(index: m_index);
156 break;
157#endif
158#if QT_CONFIG(whatsthis)
159 case QAccessible::Help:
160 str = m_parent->tabWhatsThis(index: m_index);
161 break;
162#endif
163 default:
164 break;
165 }
166 return str;
167 }
168
169 void setText(QAccessible::Text, const QString &) override {}
170
171 QAccessibleInterface *parent() const override {
172 return QAccessible::queryAccessibleInterface(m_parent.data());
173 }
174 QAccessibleInterface *child(int) const override { return nullptr; }
175
176 // action interface
177 QStringList actionNames() const override
178 {
179 return QStringList(pressAction());
180 }
181
182 void doAction(const QString &actionName) override
183 {
184 if (isValid() && actionName == pressAction())
185 m_parent->setCurrentIndex(m_index);
186 }
187
188 QStringList keyBindingsForAction(const QString &) const override
189 {
190 return QStringList();
191 }
192
193 int index() const { return m_index; }
194
195private:
196 QPointer<QTabBar> m_parent;
197 int m_index;
198
199};
200
201/*!
202 Constructs a QAccessibleTabBar object for \a w.
203*/
204QAccessibleTabBar::QAccessibleTabBar(QWidget *w)
205: QAccessibleWidget(w, QAccessible::PageTabList)
206{
207 Q_ASSERT(tabBar());
208}
209
210QAccessibleTabBar::~QAccessibleTabBar()
211{
212 for (QAccessible::Id id : qAsConst(t&: m_childInterfaces))
213 QAccessible::deleteAccessibleInterface(uniqueId: id);
214}
215
216/*! Returns the QTabBar. */
217QTabBar *QAccessibleTabBar::tabBar() const
218{
219 return qobject_cast<QTabBar*>(object: object());
220}
221
222QAccessibleInterface* QAccessibleTabBar::focusChild() const
223{
224 for (int i = 0; i < childCount(); ++i) {
225 if (child(index: i)->state().focused)
226 return child(index: i);
227 }
228
229 return nullptr;
230}
231
232QAccessibleInterface* QAccessibleTabBar::child(int index) const
233{
234 if (QAccessible::Id id = m_childInterfaces.value(akey: index))
235 return QAccessible::accessibleInterface(uniqueId: id);
236
237 // first the tabs, then 2 buttons
238 if (index < tabBar()->count()) {
239 QAccessibleTabButton *button = new QAccessibleTabButton(tabBar(), index);
240 QAccessible::registerAccessibleInterface(iface: button);
241 m_childInterfaces.insert(akey: index, avalue: QAccessible::uniqueId(iface: button));
242 return button;
243 } else if (index >= tabBar()->count()) {
244 // left button
245 if (index - tabBar()->count() == 0) {
246 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->leftB);
247 }
248 // right button
249 if (index - tabBar()->count() == 1) {
250 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->rightB);
251 }
252 }
253 return nullptr;
254}
255
256int QAccessibleTabBar::indexOfChild(const QAccessibleInterface *child) const
257{
258 if (child->object() && child->object() == tabBar()->d_func()->leftB)
259 return tabBar()->count();
260 if (child->object() && child->object() == tabBar()->d_func()->rightB)
261 return tabBar()->count() + 1;
262 if (child->role() == QAccessible::PageTab) {
263 QAccessibleInterface *parent = child->parent();
264 if (parent == this) {
265 const QAccessibleTabButton *tabButton = static_cast<const QAccessibleTabButton *>(child);
266 return tabButton->index();
267 }
268 }
269 return -1;
270}
271
272int QAccessibleTabBar::childCount() const
273{
274 // tabs + scroll buttons
275 return tabBar()->count() + 2;
276}
277
278QString QAccessibleTabBar::text(QAccessible::Text t) const
279{
280 if (t == QAccessible::Name) {
281 const QTabBar *tBar = tabBar();
282 int idx = tBar->currentIndex();
283 QString str = tBar->accessibleTabName(index: idx);
284 if (str.isEmpty())
285 str = qt_accStripAmp(text: tBar->tabText(index: idx));
286 return str;
287 } else if (t == QAccessible::Accelerator) {
288 return qt_accHotKey(text: tabBar()->tabText(index: tabBar()->currentIndex()));
289 }
290 return QString();
291}
292
293#endif // QT_CONFIG(tabbar)
294
295#if QT_CONFIG(combobox)
296/*!
297 \class QAccessibleComboBox
298 \brief The QAccessibleComboBox class implements the QAccessibleInterface for editable and read-only combo boxes.
299 \internal
300
301 \ingroup accessibility
302*/
303
304/*!
305 Constructs a QAccessibleComboBox object for \a w.
306*/
307QAccessibleComboBox::QAccessibleComboBox(QWidget *w)
308: QAccessibleWidget(w, QAccessible::ComboBox)
309{
310 Q_ASSERT(comboBox());
311}
312
313/*!
314 Returns the combobox.
315*/
316QComboBox *QAccessibleComboBox::comboBox() const
317{
318 return qobject_cast<QComboBox*>(object: object());
319}
320
321QAccessibleInterface *QAccessibleComboBox::child(int index) const
322{
323 if (index == 0) {
324 QAbstractItemView *view = comboBox()->view();
325 //QWidget *parent = view ? view->parentWidget() : 0;
326 return QAccessible::queryAccessibleInterface(view);
327 } else if (index == 1 && comboBox()->isEditable()) {
328 return QAccessible::queryAccessibleInterface(comboBox()->lineEdit());
329 }
330 return nullptr;
331}
332
333int QAccessibleComboBox::childCount() const
334{
335 // list and text edit
336 return comboBox()->isEditable() ? 2 : 1;
337}
338
339QAccessibleInterface *QAccessibleComboBox::childAt(int x, int y) const
340{
341 if (comboBox()->isEditable() && comboBox()->lineEdit()->rect().contains(ax: x, ay: y))
342 return child(index: 1);
343 return nullptr;
344}
345
346int QAccessibleComboBox::indexOfChild(const QAccessibleInterface *child) const
347{
348 if (comboBox()->view() == child->object())
349 return 0;
350 if (comboBox()->isEditable() && comboBox()->lineEdit() == child->object())
351 return 1;
352 return -1;
353}
354
355/*! \reimp */
356QString QAccessibleComboBox::text(QAccessible::Text t) const
357{
358 QString str;
359
360 switch (t) {
361 case QAccessible::Name:
362#ifndef Q_OS_UNIX // on Linux we use relations for this, name is text (fall through to Value)
363 str = QAccessibleWidget::text(t);
364 break;
365#endif
366 case QAccessible::Value:
367 if (comboBox()->isEditable())
368 str = comboBox()->lineEdit()->text();
369 else
370 str = comboBox()->currentText();
371 break;
372#ifndef QT_NO_SHORTCUT
373 case QAccessible::Accelerator:
374 str = QKeySequence(Qt::Key_Down).toString(format: QKeySequence::NativeText);
375 break;
376#endif
377 default:
378 break;
379 }
380 if (str.isEmpty())
381 str = QAccessibleWidget::text(t);
382 return str;
383}
384
385QStringList QAccessibleComboBox::actionNames() const
386{
387 return QStringList() << showMenuAction() << pressAction();
388}
389
390QString QAccessibleComboBox::localizedActionDescription(const QString &actionName) const
391{
392 if (actionName == showMenuAction() || actionName == pressAction())
393 return QComboBox::tr(s: "Open the combo box selection popup");
394 return QString();
395}
396
397void QAccessibleComboBox::doAction(const QString &actionName)
398{
399 if (actionName == showMenuAction() || actionName == pressAction()) {
400 if (comboBox()->view()->isVisible()) {
401#if defined(Q_OS_ANDROID)
402 const auto list = child(0)->tableInterface();
403 if (list && list->selectedRowCount() > 0) {
404 comboBox()->setCurrentIndex(list->selectedRows().at(0));
405 }
406 comboBox()->setFocus();
407#endif
408 comboBox()->hidePopup();
409 } else {
410 comboBox()->showPopup();
411#if defined(Q_OS_ANDROID)
412 const auto list = child(0)->tableInterface();
413 if (list && list->selectedRowCount() > 0) {
414 auto selectedCells = list->selectedCells();
415 QAccessibleEvent ev(selectedCells.at(0),QAccessible::Focus);
416 QAccessible::updateAccessibility(&ev);
417 }
418#endif
419 }
420 }
421}
422
423QStringList QAccessibleComboBox::keyBindingsForAction(const QString &/*actionName*/) const
424{
425 return QStringList();
426}
427
428#endif // QT_CONFIG(combobox)
429
430#if QT_CONFIG(scrollarea)
431// ======================= QAccessibleAbstractScrollArea =======================
432QAccessibleAbstractScrollArea::QAccessibleAbstractScrollArea(QWidget *widget)
433 : QAccessibleWidget(widget, QAccessible::Client)
434{
435 Q_ASSERT(qobject_cast<QAbstractScrollArea *>(widget));
436}
437
438QAccessibleInterface *QAccessibleAbstractScrollArea::child(int index) const
439{
440 return QAccessible::queryAccessibleInterface(accessibleChildren().at(i: index));
441}
442
443int QAccessibleAbstractScrollArea::childCount() const
444{
445 return accessibleChildren().count();
446}
447
448int QAccessibleAbstractScrollArea::indexOfChild(const QAccessibleInterface *child) const
449{
450 if (!child || !child->object())
451 return -1;
452 return accessibleChildren().indexOf(t: qobject_cast<QWidget *>(o: child->object()));
453}
454
455bool QAccessibleAbstractScrollArea::isValid() const
456{
457 return (QAccessibleWidget::isValid() && abstractScrollArea() && abstractScrollArea()->viewport());
458}
459
460QAccessibleInterface *QAccessibleAbstractScrollArea::childAt(int x, int y) const
461{
462 if (!abstractScrollArea()->isVisible())
463 return nullptr;
464
465 for (int i = 0; i < childCount(); ++i) {
466 QPoint wpos = accessibleChildren().at(i)->mapToGlobal(QPoint(0, 0));
467 QRect rect = QRect(wpos, accessibleChildren().at(i)->size());
468 if (rect.contains(ax: x, ay: y))
469 return child(index: i);
470 }
471 return nullptr;
472}
473
474QAbstractScrollArea *QAccessibleAbstractScrollArea::abstractScrollArea() const
475{
476 return static_cast<QAbstractScrollArea *>(object());
477}
478
479QWidgetList QAccessibleAbstractScrollArea::accessibleChildren() const
480{
481 QWidgetList children;
482
483 // Viewport.
484 QWidget * viewport = abstractScrollArea()->viewport();
485 if (viewport)
486 children.append(t: viewport);
487
488 // Horizontal scrollBar container.
489 QScrollBar *horizontalScrollBar = abstractScrollArea()->horizontalScrollBar();
490 if (horizontalScrollBar && horizontalScrollBar->isVisible()) {
491 children.append(t: horizontalScrollBar->parentWidget());
492 }
493
494 // Vertical scrollBar container.
495 QScrollBar *verticalScrollBar = abstractScrollArea()->verticalScrollBar();
496 if (verticalScrollBar && verticalScrollBar->isVisible()) {
497 children.append(t: verticalScrollBar->parentWidget());
498 }
499
500 // CornerWidget.
501 QWidget *cornerWidget = abstractScrollArea()->cornerWidget();
502 if (cornerWidget && cornerWidget->isVisible())
503 children.append(t: cornerWidget);
504
505 return children;
506}
507
508QAccessibleAbstractScrollArea::AbstractScrollAreaElement
509QAccessibleAbstractScrollArea::elementType(QWidget *widget) const
510{
511 if (!widget)
512 return Undefined;
513
514 if (widget == abstractScrollArea())
515 return Self;
516 if (widget == abstractScrollArea()->viewport())
517 return Viewport;
518 if (widget->objectName() == QLatin1String("qt_scrollarea_hcontainer"))
519 return HorizontalContainer;
520 if (widget->objectName() == QLatin1String("qt_scrollarea_vcontainer"))
521 return VerticalContainer;
522 if (widget == abstractScrollArea()->cornerWidget())
523 return CornerWidget;
524
525 return Undefined;
526}
527
528bool QAccessibleAbstractScrollArea::isLeftToRight() const
529{
530 return abstractScrollArea()->isLeftToRight();
531}
532
533// ======================= QAccessibleScrollArea ===========================
534QAccessibleScrollArea::QAccessibleScrollArea(QWidget *widget)
535 : QAccessibleAbstractScrollArea(widget)
536{
537 Q_ASSERT(qobject_cast<QScrollArea *>(widget));
538}
539#endif // QT_CONFIG(scrollarea)
540
541QT_END_NAMESPACE
542
543#endif // QT_NO_ACCESSIBILITY
544

source code of qtbase/src/widgets/accessible/complexwidgets.cpp