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 QtWidgets module 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 "qaccessiblewidget.h"
41 
42 #ifndef QT_NO_ACCESSIBILITY
43 
44 #include "qaction.h"
45 #include "qapplication.h"
46 #if QT_CONFIG(groupbox)
47 #include "qgroupbox.h"
48 #endif
49 #if QT_CONFIG(label)
50 #include "qlabel.h"
51 #endif
52 #include "qtooltip.h"
53 #if QT_CONFIG(whatsthis)
54 #include "qwhatsthis.h"
55 #endif
56 #include "qwidget.h"
57 #include "qdebug.h"
58 #include <qmath.h>
59 #if QT_CONFIG(rubberband)
60 #include <QRubberBand>
61 #endif
62 #include <QFocusFrame>
63 #if QT_CONFIG(menu)
64 #include <QMenu>
65 #endif
66 #include <QtWidgets/private/qwidget_p.h>
67 
68 QT_BEGIN_NAMESPACE
69 
childWidgets(const QWidget * widget)70 static QList<QWidget*> childWidgets(const QWidget *widget)
71 {
72     QList<QWidget*> widgets;
73     for (QObject *o : widget->children()) {
74         QWidget *w = qobject_cast<QWidget *>(o);
75         if (w && !w->isWindow()
76             && !qobject_cast<QFocusFrame*>(w)
77 #if QT_CONFIG(menu)
78             && !qobject_cast<QMenu*>(w)
79 #endif
80             && w->objectName() != QLatin1String("qt_rubberband")
81             && w->objectName() != QLatin1String("qt_spinbox_lineedit"))
82             widgets.append(w);
83     }
84     return widgets;
85 }
86 
buddyString(const QWidget * widget)87 static QString buddyString(const QWidget *widget)
88 {
89     if (!widget)
90         return QString();
91     QWidget *parent = widget->parentWidget();
92     if (!parent)
93         return QString();
94 #if QT_CONFIG(shortcut) && QT_CONFIG(label)
95     for (QObject *o : parent->children()) {
96         QLabel *label = qobject_cast<QLabel*>(o);
97         if (label && label->buddy() == widget)
98             return label->text();
99     }
100 #endif
101 
102 #if QT_CONFIG(groupbox)
103     QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
104     if (groupbox)
105         return groupbox->title();
106 #endif
107 
108     return QString();
109 }
110 
111 /* This function will return the offset of the '&' in the text that would be
112    preceding the accelerator character.
113    If this text does not have an accelerator, -1 will be returned. */
qt_accAmpIndex(const QString & text)114 static int qt_accAmpIndex(const QString &text)
115 {
116 #ifndef QT_NO_SHORTCUT
117     if (text.isEmpty())
118         return -1;
119 
120     int fa = 0;
121     while ((fa = text.indexOf(QLatin1Char('&'), fa)) != -1) {
122         ++fa;
123         if (fa < text.length()) {
124             // ignore "&&"
125             if (text.at(fa) == QLatin1Char('&')) {
126 
127                 ++fa;
128                 continue;
129             } else {
130                 return fa - 1;
131                 break;
132             }
133         }
134     }
135 
136     return -1;
137 #else
138     Q_UNUSED(text);
139     return -1;
140 #endif
141 }
142 
qt_accStripAmp(const QString & text)143 QString qt_accStripAmp(const QString &text)
144 {
145     QString newText(text);
146     int ampIndex = qt_accAmpIndex(newText);
147     if (ampIndex != -1)
148         newText.remove(ampIndex, 1);
149 
150     return newText.replace(QLatin1String("&&"), QLatin1String("&"));
151 }
152 
qt_accHotKey(const QString & text)153 QString qt_accHotKey(const QString &text)
154 {
155 #ifndef QT_NO_SHORTCUT
156     int ampIndex = qt_accAmpIndex(text);
157     if (ampIndex != -1)
158         return QKeySequence(Qt::ALT).toString(QKeySequence::NativeText) + text.at(ampIndex + 1);
159 #else
160     Q_UNUSED(text)
161 #endif
162 
163     return QString();
164 }
165 
166 // ### inherit QAccessibleObjectPrivate
167 class QAccessibleWidgetPrivate
168 {
169 public:
QAccessibleWidgetPrivate()170     QAccessibleWidgetPrivate()
171         :role(QAccessible::Client)
172     {}
173 
174     QAccessible::Role role;
175     QString name;
176     QStringList primarySignals;
177 };
178 
179 /*!
180     \class QAccessibleWidget
181     \brief The QAccessibleWidget class implements the QAccessibleInterface for QWidgets.
182 
183     \ingroup accessibility
184     \inmodule QtWidgets
185 
186     This class is part of \l {Accessibility for QWidget Applications}.
187 
188     This class is convenient to use as a base class for custom
189     implementations of QAccessibleInterfaces that provide information
190     about widget objects.
191 
192     The class provides functions to retrieve the parentObject() (the
193     widget's parent widget), and the associated widget(). Controlling
194     signals can be added with addControllingSignal(), and setters are
195     provided for various aspects of the interface implementation, for
196     example setValue(), setDescription(), setAccelerator(), and
197     setHelp().
198 
199     \sa QAccessible, QAccessibleObject
200 */
201 
202 /*!
203     Creates a QAccessibleWidget object for widget \a w.
204     \a role and \a name are optional parameters that set the object's
205     role and name properties.
206 */
QAccessibleWidget(QWidget * w,QAccessible::Role role,const QString & name)207 QAccessibleWidget::QAccessibleWidget(QWidget *w, QAccessible::Role role, const QString &name)
208 : QAccessibleObject(w)
209 {
210     Q_ASSERT(widget());
211     d = new QAccessibleWidgetPrivate();
212     d->role = role;
213     d->name = name;
214 }
215 
216 /*! \reimp */
isValid() const217 bool QAccessibleWidget::isValid() const
218 {
219     if (!object() || static_cast<QWidget *>(object())->d_func()->data.in_destructor)
220         return false;
221     return QAccessibleObject::isValid();
222 }
223 
224 /*! \reimp */
window() const225 QWindow *QAccessibleWidget::window() const
226 {
227     const QWidget *w = widget();
228     Q_ASSERT(w);
229     QWindow *result = w->windowHandle();
230     if (!result) {
231         if (const QWidget *nativeParent = w->nativeParentWidget())
232             result = nativeParent->windowHandle();
233     }
234     return result;
235 }
236 
237 /*!
238     Destroys this object.
239 */
~QAccessibleWidget()240 QAccessibleWidget::~QAccessibleWidget()
241 {
242     delete d;
243 }
244 
245 /*!
246     Returns the associated widget.
247 */
widget() const248 QWidget *QAccessibleWidget::widget() const
249 {
250     return qobject_cast<QWidget*>(object());
251 }
252 
253 /*!
254     Returns the associated widget's parent object, which is either the
255     parent widget, or qApp for top-level widgets.
256 */
parentObject() const257 QObject *QAccessibleWidget::parentObject() const
258 {
259     QWidget *w = widget();
260     if (!w || w->isWindow() || !w->parentWidget())
261         return qApp;
262     return w->parent();
263 }
264 
265 /*! \reimp */
rect() const266 QRect QAccessibleWidget::rect() const
267 {
268     QWidget *w = widget();
269     if (!w->isVisible())
270         return QRect();
271     QPoint wpos = w->mapToGlobal(QPoint(0, 0));
272 
273     return QRect(wpos.x(), wpos.y(), w->width(), w->height());
274 }
275 
276 /*!
277     Registers \a signal as a controlling signal.
278 
279     An object is a Controller to any other object connected to a
280     controlling signal.
281 */
addControllingSignal(const QString & signal)282 void QAccessibleWidget::addControllingSignal(const QString &signal)
283 {
284     QByteArray s = QMetaObject::normalizedSignature(signal.toLatin1());
285     if (Q_UNLIKELY(object()->metaObject()->indexOfSignal(s) < 0))
286         qWarning("Signal %s unknown in %s", s.constData(), object()->metaObject()->className());
287     d->primarySignals << QLatin1String(s);
288 }
289 
isAncestor(const QObject * obj,const QObject * child)290 static inline bool isAncestor(const QObject *obj, const QObject *child)
291 {
292     while (child) {
293         if (child == obj)
294             return true;
295         child = child->parent();
296     }
297     return false;
298 }
299 
300 /*! \reimp */
301 QVector<QPair<QAccessibleInterface*, QAccessible::Relation> >
relations(QAccessible::Relation match) const302 QAccessibleWidget::relations(QAccessible::Relation match /*= QAccessible::AllRelations*/) const
303 {
304     QVector<QPair<QAccessibleInterface*, QAccessible::Relation> > rels;
305     if (match & QAccessible::Label) {
306         const QAccessible::Relation rel = QAccessible::Label;
307         if (QWidget *parent = widget()->parentWidget()) {
308 #if QT_CONFIG(shortcut) && QT_CONFIG(label)
309             // first check for all siblings that are labels to us
310             // ideally we would go through all objects and check, but that
311             // will be too expensive
312             const QList<QWidget*> kids = childWidgets(parent);
313             for (QWidget *kid : kids) {
314                 if (QLabel *labelSibling = qobject_cast<QLabel*>(kid)) {
315                     if (labelSibling->buddy() == widget()) {
316                         QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(labelSibling);
317                         rels.append(qMakePair(iface, rel));
318                     }
319                 }
320             }
321 #endif
322 #if QT_CONFIG(groupbox)
323             QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
324             if (groupbox && !groupbox->title().isEmpty()) {
325                 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(groupbox);
326                 rels.append(qMakePair(iface, rel));
327             }
328 #endif
329         }
330     }
331 
332     if (match & QAccessible::Controlled) {
333         QObjectList allReceivers;
334         QObject *connectionObject = object();
335         for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
336             const QObjectList receivers = connectionObject->d_func()->receiverList(d->primarySignals.at(sig).toLatin1());
337             allReceivers += receivers;
338         }
339 
340         allReceivers.removeAll(object());  //### The object might connect to itself internally
341 
342         for (int i = 0; i < allReceivers.count(); ++i) {
343             const QAccessible::Relation rel = QAccessible::Controlled;
344             QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(allReceivers.at(i));
345             if (iface)
346                 rels.append(qMakePair(iface, rel));
347         }
348     }
349 
350     return rels;
351 }
352 
353 /*! \reimp */
parent() const354 QAccessibleInterface *QAccessibleWidget::parent() const
355 {
356     return QAccessible::queryAccessibleInterface(parentObject());
357 }
358 
359 /*! \reimp */
child(int index) const360 QAccessibleInterface *QAccessibleWidget::child(int index) const
361 {
362     Q_ASSERT(widget());
363     QWidgetList childList = childWidgets(widget());
364     if (index >= 0 && index < childList.size())
365         return QAccessible::queryAccessibleInterface(childList.at(index));
366     return nullptr;
367 }
368 
369 /*! \reimp */
focusChild() const370 QAccessibleInterface *QAccessibleWidget::focusChild() const
371 {
372     if (widget()->hasFocus())
373         return QAccessible::queryAccessibleInterface(object());
374 
375     QWidget *fw = widget()->focusWidget();
376     if (!fw)
377         return nullptr;
378 
379     if (isAncestor(widget(), fw)) {
380         QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(fw);
381         if (!iface || iface == this || !iface->focusChild())
382             return iface;
383         return iface->focusChild();
384     }
385     return nullptr;
386 }
387 
388 /*! \reimp */
childCount() const389 int QAccessibleWidget::childCount() const
390 {
391     QWidgetList cl = childWidgets(widget());
392     return cl.size();
393 }
394 
395 /*! \reimp */
indexOfChild(const QAccessibleInterface * child) const396 int QAccessibleWidget::indexOfChild(const QAccessibleInterface *child) const
397 {
398     if (!child)
399         return -1;
400     QWidgetList cl = childWidgets(widget());
401     return cl.indexOf(qobject_cast<QWidget *>(child->object()));
402 }
403 
404 // from qwidget.cpp
405 extern QString qt_setWindowTitle_helperHelper(const QString &, const QWidget*);
406 
407 /*! \reimp */
text(QAccessible::Text t) const408 QString QAccessibleWidget::text(QAccessible::Text t) const
409 {
410     QString str;
411 
412     switch (t) {
413     case QAccessible::Name:
414         if (!d->name.isEmpty()) {
415             str = d->name;
416         } else if (!widget()->accessibleName().isEmpty()) {
417             str = widget()->accessibleName();
418         } else if (widget()->isWindow()) {
419             if (widget()->isMinimized())
420                 str = qt_setWindowTitle_helperHelper(widget()->windowIconText(), widget());
421             else
422                 str = qt_setWindowTitle_helperHelper(widget()->windowTitle(), widget());
423         } else {
424             str = qt_accStripAmp(buddyString(widget()));
425         }
426         break;
427     case QAccessible::Description:
428         str = widget()->accessibleDescription();
429 #ifndef QT_NO_TOOLTIP
430         if (str.isEmpty())
431             str = widget()->toolTip();
432 #endif
433         break;
434     case QAccessible::Help:
435 #if QT_CONFIG(whatsthis)
436         str = widget()->whatsThis();
437 #endif
438         break;
439     case QAccessible::Accelerator:
440         str = qt_accHotKey(buddyString(widget()));
441         break;
442     case QAccessible::Value:
443         break;
444     default:
445         break;
446     }
447     return str;
448 }
449 
450 /*! \reimp */
actionNames() const451 QStringList QAccessibleWidget::actionNames() const
452 {
453     QStringList names;
454     if (widget()->isEnabled()) {
455         if (widget()->focusPolicy() != Qt::NoFocus)
456             names << setFocusAction();
457     }
458     return names;
459 }
460 
461 /*! \reimp */
doAction(const QString & actionName)462 void QAccessibleWidget::doAction(const QString &actionName)
463 {
464     if (!widget()->isEnabled())
465         return;
466 
467     if (actionName == setFocusAction()) {
468         if (widget()->isWindow())
469             widget()->activateWindow();
470         widget()->setFocus();
471     }
472 }
473 
474 /*! \reimp */
keyBindingsForAction(const QString &) const475 QStringList QAccessibleWidget::keyBindingsForAction(const QString & /* actionName */) const
476 {
477     return QStringList();
478 }
479 
480 /*! \reimp */
role() const481 QAccessible::Role QAccessibleWidget::role() const
482 {
483     return d->role;
484 }
485 
486 /*! \reimp */
state() const487 QAccessible::State QAccessibleWidget::state() const
488 {
489     QAccessible::State state;
490 
491     QWidget *w = widget();
492     if (w->testAttribute(Qt::WA_WState_Visible) == false)
493         state.invisible = true;
494     if (w->focusPolicy() != Qt::NoFocus)
495         state.focusable = true;
496     if (w->hasFocus())
497         state.focused = true;
498     if (!w->isEnabled())
499         state.disabled = true;
500     if (w->isWindow()) {
501         if (w->windowFlags() & Qt::WindowSystemMenuHint)
502             state.movable = true;
503         if (w->minimumSize() != w->maximumSize())
504             state.sizeable = true;
505         if (w->isActiveWindow())
506             state.active = true;
507     }
508 
509     return state;
510 }
511 
512 /*! \reimp */
foregroundColor() const513 QColor QAccessibleWidget::foregroundColor() const
514 {
515     return widget()->palette().color(widget()->foregroundRole());
516 }
517 
518 /*! \reimp */
backgroundColor() const519 QColor QAccessibleWidget::backgroundColor() const
520 {
521     return widget()->palette().color(widget()->backgroundRole());
522 }
523 
524 /*! \reimp */
interface_cast(QAccessible::InterfaceType t)525 void *QAccessibleWidget::interface_cast(QAccessible::InterfaceType t)
526 {
527     if (t == QAccessible::ActionInterface)
528        return static_cast<QAccessibleActionInterface*>(this);
529     return nullptr;
530 }
531 
532 QT_END_NAMESPACE
533 
534 #endif //QT_NO_ACCESSIBILITY
535