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#if QT_CONFIG(tooltip)
61#include <qtooltip.h>
62#endif
63#if QT_CONFIG(whatsthis)
64#include <qwhatsthis.h>
65#endif
66#include <QAbstractScrollArea>
67#if QT_CONFIG(scrollarea)
68#include <QScrollArea>
69#endif
70#if QT_CONFIG(scrollbar)
71#include <QScrollBar>
72#endif
73#include <QDebug>
74
75#ifndef QT_NO_ACCESSIBILITY
76
77QT_BEGIN_NAMESPACE
78
79QString qt_accStripAmp(const QString &text);
80QString qt_accHotKey(const QString &text);
81
82#if QT_CONFIG(tabbar)
83/*!
84 \class QAccessibleTabBar
85 \brief The QAccessibleTabBar class implements the QAccessibleInterface for tab bars.
86 \internal
87
88 \ingroup accessibility
89*/
90
91class QAccessibleTabButton: public QAccessibleInterface, public QAccessibleActionInterface
92{
93public:
94 QAccessibleTabButton(QTabBar *parent, int index)
95 : m_parent(parent), m_index(index)
96 {}
97
98 void *interface_cast(QAccessible::InterfaceType t) override {
99 if (t == QAccessible::ActionInterface) {
100 return static_cast<QAccessibleActionInterface*>(this);
101 }
102 return nullptr;
103 }
104
105 QObject *object() const override { return nullptr; }
106 QAccessible::Role role() const override { return QAccessible::PageTab; }
107 QAccessible::State state() const override {
108 if (!isValid()) {
109 QAccessible::State s;
110 s.invalid = true;
111 return s;
112 }
113
114 QAccessible::State s = parent()->state();
115 s.focused = (m_index == m_parent->currentIndex());
116 return s;
117 }
118 QRect rect() const override {
119 if (!isValid())
120 return QRect();
121
122 QPoint tp = m_parent->mapToGlobal(QPoint(0,0));
123 QRect rec = m_parent->tabRect(m_index);
124 rec = QRect(tp.x() + rec.x(), tp.y() + rec.y(), rec.width(), rec.height());
125 return rec;
126 }
127
128 bool isValid() const override {
129 if (m_parent) {
130 if (static_cast<QWidget *>(m_parent.data())->d_func()->data.in_destructor)
131 return false;
132 return m_parent->count() > m_index;
133 }
134 return false;
135 }
136
137 QAccessibleInterface *childAt(int, int) const override { return nullptr; }
138 int childCount() const override { return 0; }
139 int indexOfChild(const QAccessibleInterface *) const override { return -1; }
140
141 QString text(QAccessible::Text t) const override
142 {
143 if (!isValid())
144 return QString();
145 QString str;
146 switch (t) {
147 case QAccessible::Name:
148 str = m_parent->accessibleTabName(m_index);
149 if (str.isEmpty())
150 str = qt_accStripAmp(m_parent->tabText(m_index));
151 break;
152 case QAccessible::Accelerator:
153 str = qt_accHotKey(m_parent->tabText(m_index));
154 break;
155#if QT_CONFIG(tooltip)
156 case QAccessible::Description:
157 str = m_parent->tabToolTip(m_index);
158 break;
159#endif
160#if QT_CONFIG(whatsthis)
161 case QAccessible::Help:
162 str = m_parent->tabWhatsThis(m_index);
163 break;
164#endif
165 default:
166 break;
167 }
168 return str;
169 }
170
171 void setText(QAccessible::Text, const QString &) override {}
172
173 QAccessibleInterface *parent() const override {
174 return QAccessible::queryAccessibleInterface(m_parent.data());
175 }
176 QAccessibleInterface *child(int) const override { return nullptr; }
177
178 // action interface
179 QStringList actionNames() const override
180 {
181 return QStringList(pressAction());
182 }
183
184 void doAction(const QString &actionName) override
185 {
186 if (isValid() && actionName == pressAction())
187 m_parent->setCurrentIndex(m_index);
188 }
189
190 QStringList keyBindingsForAction(const QString &) const override
191 {
192 return QStringList();
193 }
194
195 int index() const { return m_index; }
196
197private:
198 QPointer<QTabBar> m_parent;
199 int m_index;
200
201};
202
203/*!
204 Constructs a QAccessibleTabBar object for \a w.
205*/
206QAccessibleTabBar::QAccessibleTabBar(QWidget *w)
207: QAccessibleWidget(w, QAccessible::PageTabList)
208{
209 Q_ASSERT(tabBar());
210}
211
212QAccessibleTabBar::~QAccessibleTabBar()
213{
214 for (QAccessible::Id id : qAsConst(m_childInterfaces))
215 QAccessible::deleteAccessibleInterface(id);
216}
217
218/*! Returns the QTabBar. */
219QTabBar *QAccessibleTabBar::tabBar() const
220{
221 return qobject_cast<QTabBar*>(object());
222}
223
224QAccessibleInterface* QAccessibleTabBar::focusChild() const
225{
226 for (int i = 0; i < childCount(); ++i) {
227 if (child(i)->state().focused)
228 return child(i);
229 }
230
231 return nullptr;
232}
233
234QAccessibleInterface* QAccessibleTabBar::child(int index) const
235{
236 if (QAccessible::Id id = m_childInterfaces.value(index))
237 return QAccessible::accessibleInterface(id);
238
239 // first the tabs, then 2 buttons
240 if (index < tabBar()->count()) {
241 QAccessibleTabButton *button = new QAccessibleTabButton(tabBar(), index);
242 QAccessible::registerAccessibleInterface(button);
243 m_childInterfaces.insert(index, QAccessible::uniqueId(button));
244 return button;
245 } else if (index >= tabBar()->count()) {
246 // left button
247 if (index - tabBar()->count() == 0) {
248 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->leftB);
249 }
250 // right button
251 if (index - tabBar()->count() == 1) {
252 return QAccessible::queryAccessibleInterface(tabBar()->d_func()->rightB);
253 }
254 }
255 return nullptr;
256}
257
258int QAccessibleTabBar::indexOfChild(const QAccessibleInterface *child) const
259{
260 if (child->object() && child->object() == tabBar()->d_func()->leftB)
261 return tabBar()->count();
262 if (child->object() && child->object() == tabBar()->d_func()->rightB)
263 return tabBar()->count() + 1;
264 if (child->role() == QAccessible::PageTab) {
265 QAccessibleInterface *parent = child->parent();
266 if (parent == this) {
267 const QAccessibleTabButton *tabButton = static_cast<const QAccessibleTabButton *>(child);
268 return tabButton->index();
269 }
270 }
271 return -1;
272}
273
274int QAccessibleTabBar::childCount() const
275{
276 // tabs + scroll buttons
277 return tabBar()->count() + 2;
278}
279
280QString QAccessibleTabBar::text(QAccessible::Text t) const
281{
282 if (t == QAccessible::Name) {
283 const QTabBar *tBar = tabBar();
284 int idx = tBar->currentIndex();
285 QString str = tBar->accessibleTabName(idx);
286 if (str.isEmpty())
287 str = qt_accStripAmp(tBar->tabText(idx));
288 return str;
289 } else if (t == QAccessible::Accelerator) {
290 return qt_accHotKey(tabBar()->tabText(tabBar()->currentIndex()));
291 }
292 return QString();
293}
294
295#endif // QT_CONFIG(tabbar)
296
297#if QT_CONFIG(combobox)
298/*!
299 \class QAccessibleComboBox
300 \brief The QAccessibleComboBox class implements the QAccessibleInterface for editable and read-only combo boxes.
301 \internal
302
303 \ingroup accessibility
304*/
305
306/*!
307 Constructs a QAccessibleComboBox object for \a w.
308*/
309QAccessibleComboBox::QAccessibleComboBox(QWidget *w)
310: QAccessibleWidget(w, QAccessible::ComboBox)
311{
312 Q_ASSERT(comboBox());
313}
314
315/*!
316 Returns the combobox.
317*/
318QComboBox *QAccessibleComboBox::comboBox() const
319{
320 return qobject_cast<QComboBox*>(object());
321}
322
323QAccessibleInterface *QAccessibleComboBox::child(int index) const
324{
325 if (index == 0) {
326 QAbstractItemView *view = comboBox()->view();
327 //QWidget *parent = view ? view->parentWidget() : 0;
328 return QAccessible::queryAccessibleInterface(view);
329 } else if (index == 1 && comboBox()->isEditable()) {
330 return QAccessible::queryAccessibleInterface(comboBox()->lineEdit());
331 }
332 return nullptr;
333}
334
335int QAccessibleComboBox::childCount() const
336{
337 // list and text edit
338 return comboBox()->isEditable() ? 2 : 1;
339}
340
341QAccessibleInterface *QAccessibleComboBox::childAt(int x, int y) const
342{
343 if (comboBox()->isEditable() && comboBox()->lineEdit()->rect().contains(x, y))
344 return child(1);
345 return nullptr;
346}
347
348int QAccessibleComboBox::indexOfChild(const QAccessibleInterface *child) const
349{
350 if (comboBox()->view() == child->object())
351 return 0;
352 if (comboBox()->isEditable() && comboBox()->lineEdit() == child->object())
353 return 1;
354 return -1;
355}
356
357/*! \reimp */
358QString QAccessibleComboBox::text(QAccessible::Text t) const
359{
360 QString str;
361
362 switch (t) {
363 case QAccessible::Name:
364#ifndef Q_OS_UNIX // on Linux we use relations for this, name is text (fall through to Value)
365 str = QAccessibleWidget::text(t);
366 break;
367#endif
368 case QAccessible::Value:
369 if (comboBox()->isEditable())
370 str = comboBox()->lineEdit()->text();
371 else
372 str = comboBox()->currentText();
373 break;
374#ifndef QT_NO_SHORTCUT
375 case QAccessible::Accelerator:
376 str = QKeySequence(Qt::Key_Down).toString(QKeySequence::NativeText);
377 break;
378#endif
379 default:
380 break;
381 }
382 if (str.isEmpty())
383 str = QAccessibleWidget::text(t);
384 return str;
385}
386
387QStringList QAccessibleComboBox::actionNames() const
388{
389 return QStringList() << showMenuAction() << pressAction();
390}
391
392QString QAccessibleComboBox::localizedActionDescription(const QString &actionName) const
393{
394 if (actionName == showMenuAction() || actionName == pressAction())
395 return QComboBox::tr("Open the combo box selection popup");
396 return QString();
397}
398
399void QAccessibleComboBox::doAction(const QString &actionName)
400{
401 if (actionName == showMenuAction() || actionName == pressAction()) {
402 if (comboBox()->view()->isVisible()) {
403 comboBox()->hidePopup();
404 } else {
405 comboBox()->showPopup();
406 }
407 }
408}
409
410QStringList QAccessibleComboBox::keyBindingsForAction(const QString &/*actionName*/) const
411{
412 return QStringList();
413}
414
415#endif // QT_CONFIG(combobox)
416
417#if QT_CONFIG(scrollarea)
418// ======================= QAccessibleAbstractScrollArea =======================
419QAccessibleAbstractScrollArea::QAccessibleAbstractScrollArea(QWidget *widget)
420 : QAccessibleWidget(widget, QAccessible::Client)
421{
422 Q_ASSERT(qobject_cast<QAbstractScrollArea *>(widget));
423}
424
425QAccessibleInterface *QAccessibleAbstractScrollArea::child(int index) const
426{
427 return QAccessible::queryAccessibleInterface(accessibleChildren().at(index));
428}
429
430int QAccessibleAbstractScrollArea::childCount() const
431{
432 return accessibleChildren().count();
433}
434
435int QAccessibleAbstractScrollArea::indexOfChild(const QAccessibleInterface *child) const
436{
437 if (!child || !child->object())
438 return -1;
439 return accessibleChildren().indexOf(qobject_cast<QWidget *>(child->object()));
440}
441
442bool QAccessibleAbstractScrollArea::isValid() const
443{
444 return (QAccessibleWidget::isValid() && abstractScrollArea() && abstractScrollArea()->viewport());
445}
446
447QAccessibleInterface *QAccessibleAbstractScrollArea::childAt(int x, int y) const
448{
449 if (!abstractScrollArea()->isVisible())
450 return nullptr;
451
452 for (int i = 0; i < childCount(); ++i) {
453 QPoint wpos = accessibleChildren().at(i)->mapToGlobal(QPoint(0, 0));
454 QRect rect = QRect(wpos, accessibleChildren().at(i)->size());
455 if (rect.contains(x, y))
456 return child(i);
457 }
458 return nullptr;
459}
460
461QAbstractScrollArea *QAccessibleAbstractScrollArea::abstractScrollArea() const
462{
463 return static_cast<QAbstractScrollArea *>(object());
464}
465
466QWidgetList QAccessibleAbstractScrollArea::accessibleChildren() const
467{
468 QWidgetList children;
469
470 // Viewport.
471 QWidget * viewport = abstractScrollArea()->viewport();
472 if (viewport)
473 children.append(viewport);
474
475 // Horizontal scrollBar container.
476 QScrollBar *horizontalScrollBar = abstractScrollArea()->horizontalScrollBar();
477 if (horizontalScrollBar && horizontalScrollBar->isVisible()) {
478 children.append(horizontalScrollBar->parentWidget());
479 }
480
481 // Vertical scrollBar container.
482 QScrollBar *verticalScrollBar = abstractScrollArea()->verticalScrollBar();
483 if (verticalScrollBar && verticalScrollBar->isVisible()) {
484 children.append(verticalScrollBar->parentWidget());
485 }
486
487 // CornerWidget.
488 QWidget *cornerWidget = abstractScrollArea()->cornerWidget();
489 if (cornerWidget && cornerWidget->isVisible())
490 children.append(cornerWidget);
491
492 return children;
493}
494
495QAccessibleAbstractScrollArea::AbstractScrollAreaElement
496QAccessibleAbstractScrollArea::elementType(QWidget *widget) const
497{
498 if (!widget)
499 return Undefined;
500
501 if (widget == abstractScrollArea())
502 return Self;
503 if (widget == abstractScrollArea()->viewport())
504 return Viewport;
505 if (widget->objectName() == QLatin1String("qt_scrollarea_hcontainer"))
506 return HorizontalContainer;
507 if (widget->objectName() == QLatin1String("qt_scrollarea_vcontainer"))
508 return VerticalContainer;
509 if (widget == abstractScrollArea()->cornerWidget())
510 return CornerWidget;
511
512 return Undefined;
513}
514
515bool QAccessibleAbstractScrollArea::isLeftToRight() const
516{
517 return abstractScrollArea()->isLeftToRight();
518}
519
520// ======================= QAccessibleScrollArea ===========================
521QAccessibleScrollArea::QAccessibleScrollArea(QWidget *widget)
522 : QAccessibleAbstractScrollArea(widget)
523{
524 Q_ASSERT(qobject_cast<QScrollArea *>(widget));
525}
526#endif // QT_CONFIG(scrollarea)
527
528QT_END_NAMESPACE
529
530#endif // QT_NO_ACCESSIBILITY
531