source: trunk/src/gui/accessible/qaccessiblewidget.cpp@ 494

Last change on this file since 494 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 32.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qaccessiblewidget.h"
43
44#ifndef QT_NO_ACCESSIBILITY
45
46#include "qaction.h"
47#include "qapplication.h"
48#include "qgroupbox.h"
49#include "qlabel.h"
50#include "qtooltip.h"
51#include "qwhatsthis.h"
52#include "qwidget.h"
53#include "qdebug.h"
54#include <qmath.h>
55#include <QRubberBand>
56#include <QtGui/QFocusFrame>
57#include <QtGui/QMenu>
58
59QT_BEGIN_NAMESPACE
60
61static QList<QWidget*> childWidgets(const QWidget *widget)
62{
63 QList<QObject*> list = widget->children();
64 QList<QWidget*> widgets;
65 for (int i = 0; i < list.size(); ++i) {
66 QWidget *w = qobject_cast<QWidget *>(list.at(i));
67 if (w && !w->isWindow()
68 && !qobject_cast<QFocusFrame*>(w)
69#if !defined(QT_NO_MENU)
70 && !qobject_cast<QMenu*>(w)
71#endif
72 && w->objectName() != QLatin1String("qt_rubberband"))
73 widgets.append(w);
74 }
75 return widgets;
76}
77
78static QString buddyString(const QWidget *widget)
79{
80 if (!widget)
81 return QString();
82 QWidget *parent = widget->parentWidget();
83 if (!parent)
84 return QString();
85#ifndef QT_NO_SHORTCUT
86 QObjectList ol = parent->children();
87 for (int i = 0; i < ol.size(); ++i) {
88 QLabel *label = qobject_cast<QLabel*>(ol.at(i));
89 if (label && label->buddy() == widget)
90 return label->text();
91 }
92#endif
93
94#ifndef QT_NO_GROUPBOX
95 QGroupBox *groupbox = qobject_cast<QGroupBox*>(parent);
96 if (groupbox)
97 return groupbox->title();
98#endif
99
100 return QString();
101}
102
103QString Q_GUI_EXPORT qt_accStripAmp(const QString &text)
104{
105 if (text.isEmpty())
106 return text;
107
108 const QChar *ch = text.unicode();
109 int length = text.length();
110 QString str;
111 while (length > 0) {
112 if (*ch == QLatin1Char('&')) {
113 ++ch;
114 --length;
115 if (!ch)
116 --ch;
117 }
118 str += *ch;
119 ++ch;
120 --length;
121 }
122 return str;
123}
124
125QString Q_GUI_EXPORT qt_accHotKey(const QString &text)
126{
127#ifndef QT_NO_SHORTCUT
128 if (text.isEmpty())
129 return text;
130
131 int fa = 0;
132 QChar ac;
133 while ((fa = text.indexOf(QLatin1Char('&'), fa)) != -1) {
134 ++fa;
135 if (fa < text.length()) {
136 // ignore "&&"
137 if (text.at(fa) == QLatin1Char('&')) {
138 ++fa;
139 continue;
140 } else {
141 ac = text.at(fa);
142 break;
143 }
144 }
145 }
146 if (ac.isNull())
147 return QString();
148 return (QString)QKeySequence(Qt::ALT) + ac.toUpper();
149#else
150 Q_UNUSED(text);
151 return QString();
152#endif
153}
154
155class QAccessibleWidgetPrivate : public QAccessible
156{
157public:
158 QAccessibleWidgetPrivate()
159 :role(Client)
160 {}
161
162 Role role;
163 QString name;
164 QString description;
165 QString value;
166 QString help;
167 QString accelerator;
168 QStringList primarySignals;
169 const QAccessibleInterface *asking;
170};
171
172/*!
173 \class QAccessibleWidget
174 \brief The QAccessibleWidget class implements the QAccessibleInterface for QWidgets.
175
176 \ingroup accessibility
177
178 This class is convenient to use as a base class for custom
179 implementations of QAccessibleInterfaces that provide information
180 about widget objects.
181
182 The class provides functions to retrieve the parentObject() (the
183 widget's parent widget), and the associated widget(). Controlling
184 signals can be added with addControllingSignal(), and setters are
185 provided for various aspects of the interface implementation, for
186 example setValue(), setDescription(), setAccelerator(), and
187 setHelp().
188
189 \sa QAccessible, QAccessibleObject
190*/
191
192/*!
193 Creates a QAccessibleWidget object for widget \a w.
194 \a role and \a name are optional parameters that set the object's
195 role and name properties.
196*/
197QAccessibleWidget::QAccessibleWidget(QWidget *w, Role role, const QString &name)
198: QAccessibleObject(w)
199{
200 Q_ASSERT(widget());
201 d = new QAccessibleWidgetPrivate();
202 d->role = role;
203 d->name = name;
204 d->asking = 0;
205}
206
207/*!
208 Destroys this object.
209*/
210QAccessibleWidget::~QAccessibleWidget()
211{
212 delete d;
213}
214
215/*!
216 Returns the associated widget.
217*/
218QWidget *QAccessibleWidget::widget() const
219{
220 return qobject_cast<QWidget*>(object());
221}
222
223/*!
224 Returns the associated widget's parent object, which is either the
225 parent widget, or qApp for top-level widgets.
226*/
227QObject *QAccessibleWidget::parentObject() const
228{
229 QObject *parent = object()->parent();
230 if (!parent)
231 parent = qApp;
232 return parent;
233}
234
235/*! \reimp */
236int QAccessibleWidget::childAt(int x, int y) const
237{
238 QWidget *w = widget();
239 if (!w->isVisible())
240 return -1;
241 QPoint gp = w->mapToGlobal(QPoint(0, 0));
242 if (!QRect(gp.x(), gp.y(), w->width(), w->height()).contains(x, y))
243 return -1;
244
245 QWidgetList list = childWidgets(w);
246 int ccount = childCount();
247
248 // a complex child
249 if (list.size() < ccount) {
250 for (int i = 1; i <= ccount; ++i) {
251 if (rect(i).contains(x, y))
252 return i;
253 }
254 return 0;
255 }
256
257 QPoint rp = w->mapFromGlobal(QPoint(x, y));
258 for (int i = 0; i<list.size(); ++i) {
259 QWidget *child = list.at(i);
260 if (!child->isWindow() && !child->isHidden() && child->geometry().contains(rp)) {
261 return i + 1;
262 }
263 }
264 return 0;
265}
266
267/*! \reimp */
268QRect QAccessibleWidget::rect(int child) const
269{
270 if (child) {
271 qWarning("QAccessibleWidget::rect: This implementation does not support subelements! "
272 "(ID %d unknown for %s)", child, widget()->metaObject()->className());
273 }
274
275 QWidget *w = widget();
276 if (!w->isVisible())
277 return QRect();
278 QPoint wpos = w->mapToGlobal(QPoint(0, 0));
279
280 return QRect(wpos.x(), wpos.y(), w->width(), w->height());
281}
282
283QT_BEGIN_INCLUDE_NAMESPACE
284#include <private/qobject_p.h>
285QT_END_INCLUDE_NAMESPACE
286
287class QACConnectionObject : public QObject
288{
289 Q_DECLARE_PRIVATE(QObject)
290public:
291 inline bool isSender(const QObject *receiver, const char *signal) const
292 { return d_func()->isSender(receiver, signal); }
293 inline QObjectList receiverList(const char *signal) const
294 { return d_func()->receiverList(signal); }
295 inline QObjectList senderList() const
296 { return d_func()->senderList(); }
297};
298
299/*!
300 Registers \a signal as a controlling signal.
301
302 An object is a Controller to any other object connected to a
303 controlling signal.
304*/
305void QAccessibleWidget::addControllingSignal(const QString &signal)
306{
307 QByteArray s = QMetaObject::normalizedSignature(signal.toAscii());
308 if (object()->metaObject()->indexOfSignal(s) < 0)
309 qWarning("Signal %s unknown in %s", s.constData(), object()->metaObject()->className());
310 d->primarySignals << QLatin1String(s);
311}
312
313/*!
314 Sets the value of this interface implementation to \a value.
315
316 The default implementation of text() returns the set value for
317 the Value text.
318
319 Note that the object wrapped by this interface is not modified.
320*/
321void QAccessibleWidget::setValue(const QString &value)
322{
323 d->value = value;
324}
325
326/*!
327 Sets the description of this interface implementation to \a desc.
328
329 The default implementation of text() returns the set value for
330 the Description text.
331
332 Note that the object wrapped by this interface is not modified.
333*/
334void QAccessibleWidget::setDescription(const QString &desc)
335{
336 d->description = desc;
337}
338
339/*!
340 Sets the help of this interface implementation to \a help.
341
342 The default implementation of text() returns the set value for
343 the Help text.
344
345 Note that the object wrapped by this interface is not modified.
346*/
347void QAccessibleWidget::setHelp(const QString &help)
348{
349 d->help = help;
350}
351
352/*!
353 Sets the accelerator of this interface implementation to \a accel.
354
355 The default implementation of text() returns the set value for
356 the Accelerator text.
357
358 Note that the object wrapped by this interface is not modified.
359*/
360void QAccessibleWidget::setAccelerator(const QString &accel)
361{
362 d->accelerator = accel;
363}
364
365static inline bool isAncestor(const QObject *obj, const QObject *child)
366{
367 while (child) {
368 if (child == obj)
369 return true;
370 child = child->parent();
371 }
372 return false;
373}
374
375
376/*! \reimp */
377QAccessible::Relation QAccessibleWidget::relationTo(int child,
378 const QAccessibleInterface *other, int otherChild) const
379{
380 Relation relation = Unrelated;
381 if (d->asking == this) // recursive call
382 return relation;
383
384 QObject *o = other ? other->object() : 0;
385 if (!o)
386 return relation;
387
388 QWidget *focus = widget()->focusWidget();
389 if (object() == focus && isAncestor(o, focus))
390 relation |= FocusChild;
391
392 QACConnectionObject *connectionObject = (QACConnectionObject*)object();
393 for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
394 if (connectionObject->isSender(o, d->primarySignals.at(sig).toAscii())) {
395 relation |= Controller;
396 break;
397 }
398 }
399 // test for passive relationships.
400 // d->asking protects from endless recursion.
401 d->asking = this;
402 int inverse = other->relationTo(otherChild, this, child);
403 d->asking = 0;
404
405 if (inverse & Controller)
406 relation |= Controlled;
407 if (inverse & Label)
408 relation |= Labelled;
409
410 if(o == object()) {
411 if (child && !otherChild)
412 return relation | Child;
413 if (!child && otherChild)
414 return relation | Ancestor;
415 if (!child && !otherChild)
416 return relation | Self;
417 }
418
419 QObject *parent = object()->parent();
420 if (o == parent)
421 return relation | Child;
422
423 if (o->parent() == parent) {
424 relation |= Sibling;
425 QAccessibleInterface *sibIface = QAccessible::queryAccessibleInterface(o);
426 Q_ASSERT(sibIface);
427 QRect wg = rect(0);
428 QRect sg = sibIface->rect(0);
429 if (wg.intersects(sg)) {
430 QAccessibleInterface *pIface = 0;
431 sibIface->navigate(Ancestor, 1, &pIface);
432 if (pIface && !((sibIface->state(0) | state(0)) & Invisible)) {
433 int wi = pIface->indexOfChild(this);
434 int si = pIface->indexOfChild(sibIface);
435
436 if (wi > si)
437 relation |= QAccessible::Covers;
438 else
439 relation |= QAccessible::Covered;
440 }
441 delete pIface;
442 } else {
443 QPoint wc = wg.center();
444 QPoint sc = sg.center();
445 if (wc.x() < sc.x())
446 relation |= QAccessible::Left;
447 else if(wc.x() > sc.x())
448 relation |= QAccessible::Right;
449 if (wc.y() < sc.y())
450 relation |= QAccessible::Up;
451 else if (wc.y() > sc.y())
452 relation |= QAccessible::Down;
453 }
454 delete sibIface;
455
456 return relation;
457 }
458
459 if (isAncestor(o, object()))
460 return relation | Descendent;
461 if (isAncestor(object(), o))
462 return relation | Ancestor;
463
464 return relation;
465}
466
467/*! \reimp */
468int QAccessibleWidget::navigate(RelationFlag relation, int entry,
469 QAccessibleInterface **target) const
470{
471 if (!target)
472 return -1;
473
474 *target = 0;
475 QObject *targetObject = 0;
476
477 QWidgetList childList = childWidgets(widget());
478 bool complexWidget = childList.size() < childCount();
479
480 switch (relation) {
481 // Hierarchical
482 case Self:
483 targetObject = object();
484 break;
485 case Child:
486 if (complexWidget) {
487 if (entry > 0 && entry <= childCount())
488 return entry;
489 return -1;
490 }else {
491 if (entry > 0 && childList.size() >= entry)
492 targetObject = childList.at(entry - 1);
493 }
494 break;
495 case Ancestor:
496 {
497 if (entry <= 0)
498 return -1;
499 targetObject = widget()->parentWidget();
500 int i;
501 for (i = entry; i > 1 && targetObject; --i)
502 targetObject = targetObject->parent();
503 if (!targetObject && i == 1)
504 targetObject = qApp;
505 }
506 break;
507 case Sibling:
508 {
509 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(parentObject());
510 if (!iface)
511 return -1;
512
513 iface->navigate(Child, entry, target);
514 delete iface;
515 if (*target)
516 return 0;
517 }
518 break;
519
520 // Geometrical
521 case QAccessible::Left:
522 if (complexWidget && entry) {
523 if (entry < 2 || widget()->height() > widget()->width() + 20) // looks vertical
524 return -1;
525 return entry - 1;
526 }
527 // fall through
528 case QAccessible::Right:
529 if (complexWidget && entry) {
530 if (entry >= childCount() || widget()->height() > widget()->width() + 20) // looks vertical
531 return -1;
532 return entry + 1;
533 }
534 // fall through
535 case QAccessible::Up:
536 if (complexWidget && entry) {
537 if (entry < 2 || widget()->width() > widget()->height() + 20) // looks horizontal
538 return - 1;
539 return entry - 1;
540 }
541 // fall through
542 case QAccessible::Down:
543 if (complexWidget && entry) {
544 if (entry >= childCount() || widget()->width() > widget()->height() + 20) // looks horizontal
545 return - 1;
546 return entry + 1;
547 } else {
548 QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
549 if (!pIface)
550 return -1;
551
552 QRect startg = rect(0);
553 QPoint startc = startg.center();
554 QAccessibleInterface *candidate = 0;
555 int mindist = 100000;
556 int sibCount = pIface->childCount();
557 for (int i = 0; i < sibCount; ++i) {
558 QAccessibleInterface *sibling = 0;
559 pIface->navigate(Child, i+1, &sibling);
560 Q_ASSERT(sibling);
561 if ((relationTo(0, sibling, 0) & Self) || (sibling->state(0) & QAccessible::Invisible)) {
562 //ignore ourself and invisible siblings
563 delete sibling;
564 continue;
565 }
566
567 QRect sibg = sibling->rect(0);
568 QPoint sibc = sibg.center();
569 QPoint sibp;
570 QPoint startp;
571 QPoint distp;
572 switch (relation) {
573 case QAccessible::Left:
574 startp = QPoint(startg.left(), startg.top() + startg.height() / 2);
575 sibp = QPoint(sibg.right(), sibg.top() + sibg.height() / 2);
576 if (QPoint(sibc - startc).x() >= 0) {
577 delete sibling;
578 continue;
579 }
580 distp = sibp - startp;
581 break;
582 case QAccessible::Right:
583 startp = QPoint(startg.right(), startg.top() + startg.height() / 2);
584 sibp = QPoint(sibg.left(), sibg.top() + sibg.height() / 2);
585 if (QPoint(sibc - startc).x() <= 0) {
586 delete sibling;
587 continue;
588 }
589 distp = sibp - startp;
590 break;
591 case QAccessible::Up:
592 startp = QPoint(startg.left() + startg.width() / 2, startg.top());
593 sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.bottom());
594 if (QPoint(sibc - startc).y() >= 0) {
595 delete sibling;
596 continue;
597 }
598 distp = sibp - startp;
599 break;
600 case QAccessible::Down:
601 startp = QPoint(startg.left() + startg.width() / 2, startg.bottom());
602 sibp = QPoint(sibg.left() + sibg.width() / 2, sibg.top());
603 if (QPoint(sibc - startc).y() <= 0) {
604 delete sibling;
605 continue;
606 }
607 distp = sibp - startp;
608 break;
609 default:
610 break;
611 }
612
613 int dist = (int)qSqrt((qreal)distp.x() * distp.x() + distp.y() * distp.y());
614 if (dist < mindist) {
615 delete candidate;
616 candidate = sibling;
617 mindist = dist;
618 } else {
619 delete sibling;
620 }
621 }
622 delete pIface;
623 *target = candidate;
624 if (*target)
625 return 0;
626 }
627 break;
628 case Covers:
629 if (entry > 0) {
630 QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
631 if (!pIface)
632 return -1;
633
634 QRect r = rect(0);
635 int sibCount = pIface->childCount();
636 QAccessibleInterface *sibling = 0;
637 for (int i = pIface->indexOfChild(this) + 1; i <= sibCount && entry; ++i) {
638 pIface->navigate(Child, i, &sibling);
639 if (!sibling || (sibling->state(0) & Invisible)) {
640 delete sibling;
641 sibling = 0;
642 continue;
643 }
644 if (sibling->rect(0).intersects(r))
645 --entry;
646 if (!entry)
647 break;
648 delete sibling;
649 sibling = 0;
650 }
651 delete pIface;
652 *target = sibling;
653 if (*target)
654 return 0;
655 }
656 break;
657 case Covered:
658 if (entry > 0) {
659 QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
660 if (!pIface)
661 return -1;
662
663 QRect r = rect(0);
664 int index = pIface->indexOfChild(this);
665 QAccessibleInterface *sibling = 0;
666 for (int i = 1; i < index && entry; ++i) {
667 pIface->navigate(Child, i, &sibling);
668 Q_ASSERT(sibling);
669 if (!sibling || (sibling->state(0) & Invisible)) {
670 delete sibling;
671 sibling = 0;
672 continue;
673 }
674 if (sibling->rect(0).intersects(r))
675 --entry;
676 if (!entry)
677 break;
678 delete sibling;
679 sibling = 0;
680 }
681 delete pIface;
682 *target = sibling;
683 if (*target)
684 return 0;
685 }
686 break;
687
688 // Logical
689 case FocusChild:
690 {
691 if (widget()->hasFocus()) {
692 targetObject = object();
693 break;
694 }
695
696 QWidget *fw = widget()->focusWidget();
697 if (!fw)
698 return -1;
699
700 if (isAncestor(widget(), fw) || fw == widget())
701 targetObject = fw;
702 /* ###
703 QWidget *parent = fw;
704 while (parent && !targetObject) {
705 parent = parent->parentWidget();
706 if (parent == widget())
707 targetObject = fw;
708 }
709 */
710 }
711 break;
712 case Label:
713 if (entry > 0) {
714 QAccessibleInterface *pIface = QAccessible::queryAccessibleInterface(parentObject());
715 if (!pIface)
716 return -1;
717
718 // first check for all siblings that are labels to us
719 // ideally we would go through all objects and check, but that
720 // will be too expensive
721 int sibCount = pIface->childCount();
722 QAccessibleInterface *candidate = 0;
723 for (int i = 0; i < sibCount && entry; ++i) {
724 pIface->navigate(Child, i+1, &candidate);
725 Q_ASSERT(candidate);
726 if (candidate->relationTo(0, this, 0) & Label)
727 --entry;
728 if (!entry)
729 break;
730 delete candidate;
731 candidate = 0;
732 }
733 if (!candidate) {
734 if (pIface->relationTo(0, this, 0) & Label)
735 --entry;
736 if (!entry)
737 candidate = pIface;
738 }
739 if (pIface != candidate)
740 delete pIface;
741
742 *target = candidate;
743 if (*target)
744 return 0;
745 }
746 break;
747 case Labelled: // only implemented in subclasses
748 break;
749 case Controller:
750 if (entry > 0) {
751 // check all senders we are connected to,
752 // and figure out which one are controllers to us
753 QACConnectionObject *connectionObject = (QACConnectionObject*)object();
754 QObjectList allSenders = connectionObject->senderList();
755 QObjectList senders;
756 for (int s = 0; s < allSenders.size(); ++s) {
757 QObject *sender = allSenders.at(s);
758 QAccessibleInterface *candidate = QAccessible::queryAccessibleInterface(sender);
759 if (!candidate)
760 continue;
761 if (candidate->relationTo(0, this, 0)&Controller)
762 senders << sender;
763 delete candidate;
764 }
765 if (entry <= senders.size())
766 targetObject = senders.at(entry-1);
767 }
768 break;
769 case Controlled:
770 if (entry > 0) {
771 QObjectList allReceivers;
772 QACConnectionObject *connectionObject = (QACConnectionObject*)object();
773 for (int sig = 0; sig < d->primarySignals.count(); ++sig) {
774 QObjectList receivers = connectionObject->receiverList(d->primarySignals.at(sig).toAscii());
775 allReceivers += receivers;
776 }
777 if (entry <= allReceivers.size())
778 targetObject = allReceivers.at(entry-1);
779 }
780 break;
781 default:
782 break;
783 }
784
785 *target = QAccessible::queryAccessibleInterface(targetObject);
786 return *target ? 0 : -1;
787}
788
789/*! \reimp */
790int QAccessibleWidget::childCount() const