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 "qaccessiblewidgets_p.h"
41 #include "qabstracttextdocumentlayout.h"
42 #include "qapplication.h"
43 #include "qclipboard.h"
44 #include "qtextdocument.h"
45 #include "qtextobject.h"
46 #if QT_CONFIG(textedit)
47 #include "qplaintextedit.h"
48 #include "qtextedit.h"
49 #include "private/qtextedit_p.h"
50 #endif
51 #include "qtextboundaryfinder.h"
52 #if QT_CONFIG(scrollbar)
53 #include "qscrollbar.h"
54 #endif
55 #include "qdebug.h"
56 #include <QApplication>
57 #if QT_CONFIG(stackedwidget)
58 #include <QStackedWidget>
59 #endif
60 #if QT_CONFIG(toolbox)
61 #include <QToolBox>
62 #endif
63 #if QT_CONFIG(mdiarea)
64 #include <QMdiArea>
65 #include <QMdiSubWindow>
66 #endif
67 #if QT_CONFIG(dialogbuttonbox)
68 #include <QDialogButtonBox>
69 #endif
70 #include <limits.h>
71 #if QT_CONFIG(rubberband)
72 #include <QRubberBand>
73 #endif
74 #if QT_CONFIG(textbrowser)
75 #include <QTextBrowser>
76 #endif
77 #if QT_CONFIG(calendarwidget)
78 #include <QCalendarWidget>
79 #endif
80 #if QT_CONFIG(itemviews)
81 #include <QAbstractItemView>
82 #endif
83 #if QT_CONFIG(dockwidget)
84 #include <QDockWidget>
85 #include <private/qdockwidget_p.h>
86 #endif
87 #if QT_CONFIG(mainwindow)
88 #include <QMainWindow>
89 #endif
90 #include <QFocusFrame>
91 #if QT_CONFIG(menu)
92 #include <QMenu>
93 #endif
94
95 #ifndef QT_NO_ACCESSIBILITY
96
97 QT_BEGIN_NAMESPACE
98
99 QString qt_accStripAmp(const QString &text);
100 QString qt_accHotKey(const QString &text);
101
childWidgets(const QWidget * widget)102 QList<QWidget*> childWidgets(const QWidget *widget)
103 {
104 QList<QWidget*> widgets;
105 if (!widget)
106 return widgets;
107 for (QObject *o : widget->children()) {
108 QWidget *w = qobject_cast<QWidget *>(o);
109 if (!w)
110 continue;
111 QString objectName = w->objectName();
112 if (!w->isWindow()
113 && !qobject_cast<QFocusFrame*>(w)
114 #if QT_CONFIG(menu)
115 && !qobject_cast<QMenu*>(w)
116 #endif
117 && objectName != QLatin1String("qt_rubberband")
118 && objectName != QLatin1String("qt_qmainwindow_extended_splitter")) {
119 widgets.append(w);
120 }
121 }
122 return widgets;
123 }
124
125 #if QT_CONFIG(textedit) && !defined(QT_NO_CURSOR)
126
QAccessiblePlainTextEdit(QWidget * o)127 QAccessiblePlainTextEdit::QAccessiblePlainTextEdit(QWidget* o)
128 :QAccessibleTextWidget(o)
129 {
130 Q_ASSERT(widget()->inherits("QPlainTextEdit"));
131 }
132
plainTextEdit() const133 QPlainTextEdit* QAccessiblePlainTextEdit::plainTextEdit() const
134 {
135 return static_cast<QPlainTextEdit *>(widget());
136 }
137
text(QAccessible::Text t) const138 QString QAccessiblePlainTextEdit::text(QAccessible::Text t) const
139 {
140 if (t == QAccessible::Value)
141 return plainTextEdit()->toPlainText();
142
143 return QAccessibleWidget::text(t);
144 }
145
setText(QAccessible::Text t,const QString & text)146 void QAccessiblePlainTextEdit::setText(QAccessible::Text t, const QString &text)
147 {
148 if (t != QAccessible::Value) {
149 QAccessibleWidget::setText(t, text);
150 return;
151 }
152 if (plainTextEdit()->isReadOnly())
153 return;
154
155 plainTextEdit()->setPlainText(text);
156 }
157
state() const158 QAccessible::State QAccessiblePlainTextEdit::state() const
159 {
160 QAccessible::State st = QAccessibleTextWidget::state();
161 if (plainTextEdit()->isReadOnly())
162 st.readOnly = true;
163 else
164 st.editable = true;
165 return st;
166 }
167
interface_cast(QAccessible::InterfaceType t)168 void *QAccessiblePlainTextEdit::interface_cast(QAccessible::InterfaceType t)
169 {
170 if (t == QAccessible::TextInterface)
171 return static_cast<QAccessibleTextInterface*>(this);
172 else if (t == QAccessible::EditableTextInterface)
173 return static_cast<QAccessibleEditableTextInterface*>(this);
174 return QAccessibleWidget::interface_cast(t);
175 }
176
scrollBarPosition() const177 QPoint QAccessiblePlainTextEdit::scrollBarPosition() const
178 {
179 QPoint result;
180 result.setX(plainTextEdit()->horizontalScrollBar() ? plainTextEdit()->horizontalScrollBar()->sliderPosition() : 0);
181 result.setY(plainTextEdit()->verticalScrollBar() ? plainTextEdit()->verticalScrollBar()->sliderPosition() : 0);
182 return result;
183 }
184
textCursor() const185 QTextCursor QAccessiblePlainTextEdit::textCursor() const
186 {
187 return plainTextEdit()->textCursor();
188 }
189
setTextCursor(const QTextCursor & textCursor)190 void QAccessiblePlainTextEdit::setTextCursor(const QTextCursor &textCursor)
191 {
192 plainTextEdit()->setTextCursor(textCursor);
193 }
194
textDocument() const195 QTextDocument* QAccessiblePlainTextEdit::textDocument() const
196 {
197 return plainTextEdit()->document();
198 }
199
viewport() const200 QWidget* QAccessiblePlainTextEdit::viewport() const
201 {
202 return plainTextEdit()->viewport();
203 }
204
scrollToSubstring(int startIndex,int endIndex)205 void QAccessiblePlainTextEdit::scrollToSubstring(int startIndex, int endIndex)
206 {
207 //TODO: Not implemented
208 Q_UNUSED(startIndex);
209 Q_UNUSED(endIndex);
210 }
211
212
213 /*!
214 \class QAccessibleTextEdit
215 \brief The QAccessibleTextEdit class implements the QAccessibleInterface for richtext editors.
216 \internal
217 */
218
219 /*!
220 \fn QAccessibleTextEdit::QAccessibleTextEdit(QWidget *widget)
221
222 Constructs a QAccessibleTextEdit object for a \a widget.
223 */
QAccessibleTextEdit(QWidget * o)224 QAccessibleTextEdit::QAccessibleTextEdit(QWidget *o)
225 : QAccessibleTextWidget(o, QAccessible::EditableText)
226 {
227 Q_ASSERT(widget()->inherits("QTextEdit"));
228 }
229
230 /*! Returns the text edit. */
textEdit() const231 QTextEdit *QAccessibleTextEdit::textEdit() const
232 {
233 return static_cast<QTextEdit *>(widget());
234 }
235
textCursor() const236 QTextCursor QAccessibleTextEdit::textCursor() const
237 {
238 return textEdit()->textCursor();
239 }
240
textDocument() const241 QTextDocument *QAccessibleTextEdit::textDocument() const
242 {
243 return textEdit()->document();
244 }
245
setTextCursor(const QTextCursor & textCursor)246 void QAccessibleTextEdit::setTextCursor(const QTextCursor &textCursor)
247 {
248 textEdit()->setTextCursor(textCursor);
249 }
250
viewport() const251 QWidget *QAccessibleTextEdit::viewport() const
252 {
253 return textEdit()->viewport();
254 }
255
scrollBarPosition() const256 QPoint QAccessibleTextEdit::scrollBarPosition() const
257 {
258 QPoint result;
259 result.setX(textEdit()->horizontalScrollBar() ? textEdit()->horizontalScrollBar()->sliderPosition() : 0);
260 result.setY(textEdit()->verticalScrollBar() ? textEdit()->verticalScrollBar()->sliderPosition() : 0);
261 return result;
262 }
263
text(QAccessible::Text t) const264 QString QAccessibleTextEdit::text(QAccessible::Text t) const
265 {
266 if (t == QAccessible::Value)
267 return textEdit()->toPlainText();
268
269 return QAccessibleWidget::text(t);
270 }
271
setText(QAccessible::Text t,const QString & text)272 void QAccessibleTextEdit::setText(QAccessible::Text t, const QString &text)
273 {
274 if (t != QAccessible::Value) {
275 QAccessibleWidget::setText(t, text);
276 return;
277 }
278 if (textEdit()->isReadOnly())
279 return;
280
281 textEdit()->setText(text);
282 }
283
state() const284 QAccessible::State QAccessibleTextEdit::state() const
285 {
286 QAccessible::State st = QAccessibleTextWidget::state();
287 if (textEdit()->isReadOnly())
288 st.readOnly = true;
289 else
290 st.editable = true;
291 return st;
292 }
293
interface_cast(QAccessible::InterfaceType t)294 void *QAccessibleTextEdit::interface_cast(QAccessible::InterfaceType t)
295 {
296 if (t == QAccessible::TextInterface)
297 return static_cast<QAccessibleTextInterface*>(this);
298 else if (t == QAccessible::EditableTextInterface)
299 return static_cast<QAccessibleEditableTextInterface*>(this);
300 return QAccessibleWidget::interface_cast(t);
301 }
302
scrollToSubstring(int startIndex,int endIndex)303 void QAccessibleTextEdit::scrollToSubstring(int startIndex, int endIndex)
304 {
305 QTextEdit *edit = textEdit();
306
307 QTextCursor cursor = textCursor();
308 cursor.setPosition(startIndex);
309 QRect r = edit->cursorRect(cursor);
310
311 cursor.setPosition(endIndex);
312 r.setBottomRight(edit->cursorRect(cursor).bottomRight());
313
314 r.moveTo(r.x() + edit->horizontalScrollBar()->value(),
315 r.y() + edit->verticalScrollBar()->value());
316
317 // E V I L, but ensureVisible is not public
318 if (Q_UNLIKELY(!QMetaObject::invokeMethod(edit, "_q_ensureVisible", Q_ARG(QRectF, r))))
319 qWarning("AccessibleTextEdit::scrollToSubstring failed!");
320 }
321
322 #endif // QT_CONFIG(textedit) && QT_NO_CURSOR
323
324 #if QT_CONFIG(stackedwidget)
325 // ======================= QAccessibleStackedWidget ======================
QAccessibleStackedWidget(QWidget * widget)326 QAccessibleStackedWidget::QAccessibleStackedWidget(QWidget *widget)
327 : QAccessibleWidget(widget, QAccessible::LayeredPane)
328 {
329 Q_ASSERT(qobject_cast<QStackedWidget *>(widget));
330 }
331
childAt(int x,int y) const332 QAccessibleInterface *QAccessibleStackedWidget::childAt(int x, int y) const
333 {
334 if (!stackedWidget()->isVisible())
335 return nullptr;
336 QWidget *currentWidget = stackedWidget()->currentWidget();
337 if (!currentWidget)
338 return nullptr;
339 QPoint position = currentWidget->mapFromGlobal(QPoint(x, y));
340 if (currentWidget->rect().contains(position))
341 return child(stackedWidget()->currentIndex());
342 return nullptr;
343 }
344
childCount() const345 int QAccessibleStackedWidget::childCount() const
346 {
347 return stackedWidget()->count();
348 }
349
indexOfChild(const QAccessibleInterface * child) const350 int QAccessibleStackedWidget::indexOfChild(const QAccessibleInterface *child) const
351 {
352 if (!child)
353 return -1;
354
355 QWidget *widget = qobject_cast<QWidget*>(child->object());
356 return stackedWidget()->indexOf(widget);
357 }
358
child(int index) const359 QAccessibleInterface *QAccessibleStackedWidget::child(int index) const
360 {
361 if (index < 0 || index >= stackedWidget()->count())
362 return nullptr;
363 return QAccessible::queryAccessibleInterface(stackedWidget()->widget(index));
364 }
365
stackedWidget() const366 QStackedWidget *QAccessibleStackedWidget::stackedWidget() const
367 {
368 return static_cast<QStackedWidget *>(object());
369 }
370 #endif // QT_CONFIG(stackedwidget)
371
372 #if QT_CONFIG(toolbox)
373 // ======================= QAccessibleToolBox ======================
QAccessibleToolBox(QWidget * widget)374 QAccessibleToolBox::QAccessibleToolBox(QWidget *widget)
375 : QAccessibleWidget(widget, QAccessible::LayeredPane)
376 {
377 Q_ASSERT(qobject_cast<QToolBox *>(widget));
378 }
379
toolBox() const380 QToolBox * QAccessibleToolBox::toolBox() const
381 {
382 return static_cast<QToolBox *>(object());
383 }
384 #endif // QT_CONFIG(toolbox)
385
386 // ======================= QAccessibleMdiArea ======================
387 #if QT_CONFIG(mdiarea)
QAccessibleMdiArea(QWidget * widget)388 QAccessibleMdiArea::QAccessibleMdiArea(QWidget *widget)
389 : QAccessibleWidget(widget, QAccessible::LayeredPane)
390 {
391 Q_ASSERT(qobject_cast<QMdiArea *>(widget));
392 }
393
childCount() const394 int QAccessibleMdiArea::childCount() const
395 {
396 return mdiArea()->subWindowList().count();
397 }
398
child(int index) const399 QAccessibleInterface *QAccessibleMdiArea::child(int index) const
400 {
401 QList<QMdiSubWindow *> subWindows = mdiArea()->subWindowList();
402 QWidget *targetObject = subWindows.value(index);
403 if (!targetObject)
404 return nullptr;
405 return QAccessible::queryAccessibleInterface(targetObject);
406 }
407
408
indexOfChild(const QAccessibleInterface * child) const409 int QAccessibleMdiArea::indexOfChild(const QAccessibleInterface *child) const
410 {
411 if (!child || !child->object() || mdiArea()->subWindowList().isEmpty())
412 return -1;
413 if (QMdiSubWindow *window = qobject_cast<QMdiSubWindow *>(child->object())) {
414 return mdiArea()->subWindowList().indexOf(window);
415 }
416 return -1;
417 }
418
mdiArea() const419 QMdiArea *QAccessibleMdiArea::mdiArea() const
420 {
421 return static_cast<QMdiArea *>(object());
422 }
423
424 // ======================= QAccessibleMdiSubWindow ======================
QAccessibleMdiSubWindow(QWidget * widget)425 QAccessibleMdiSubWindow::QAccessibleMdiSubWindow(QWidget *widget)
426 : QAccessibleWidget(widget, QAccessible::Window)
427 {
428 Q_ASSERT(qobject_cast<QMdiSubWindow *>(widget));
429 }
430
text(QAccessible::Text textType) const431 QString QAccessibleMdiSubWindow::text(QAccessible::Text textType) const
432 {
433 if (textType == QAccessible::Name) {
434 QString title = mdiSubWindow()->windowTitle();
435 title.replace(QLatin1String("[*]"), QLatin1String(""));
436 return title;
437 }
438 return QAccessibleWidget::text(textType);
439 }
440
setText(QAccessible::Text textType,const QString & text)441 void QAccessibleMdiSubWindow::setText(QAccessible::Text textType, const QString &text)
442 {
443 if (textType == QAccessible::Name)
444 mdiSubWindow()->setWindowTitle(text);
445 else
446 QAccessibleWidget::setText(textType, text);
447 }
448
state() const449 QAccessible::State QAccessibleMdiSubWindow::state() const
450 {
451 QAccessible::State state;
452 state.focusable = true;
453 if (!mdiSubWindow()->isMaximized()) {
454 state.movable = true;
455 state.sizeable = true;
456 }
457 if (mdiSubWindow()->isAncestorOf(QApplication::focusWidget())
458 || QApplication::focusWidget() == mdiSubWindow())
459 state.focused = true;
460 if (!mdiSubWindow()->isVisible())
461 state.invisible = true;
462 if (const QWidget *parent = mdiSubWindow()->parentWidget())
463 if (!parent->contentsRect().contains(mdiSubWindow()->geometry()))
464 state.offscreen = true;
465 if (!mdiSubWindow()->isEnabled())
466 state.disabled = true;
467 return state;
468 }
469
childCount() const470 int QAccessibleMdiSubWindow::childCount() const
471 {
472 if (mdiSubWindow()->widget())
473 return 1;
474 return 0;
475 }
476
child(int index) const477 QAccessibleInterface *QAccessibleMdiSubWindow::child(int index) const
478 {
479 QMdiSubWindow *source = mdiSubWindow();
480 if (index != 0 || !source->widget())
481 return nullptr;
482
483 return QAccessible::queryAccessibleInterface(source->widget());
484 }
485
indexOfChild(const QAccessibleInterface * child) const486 int QAccessibleMdiSubWindow::indexOfChild(const QAccessibleInterface *child) const
487 {
488 if (child && child->object() && child->object() == mdiSubWindow()->widget())
489 return 0;
490 return -1;
491 }
492
rect() const493 QRect QAccessibleMdiSubWindow::rect() const
494 {
495 if (mdiSubWindow()->isHidden())
496 return QRect();
497 if (!mdiSubWindow()->parent())
498 return QAccessibleWidget::rect();
499 const QPoint pos = mdiSubWindow()->mapToGlobal(QPoint(0, 0));
500 return QRect(pos, mdiSubWindow()->size());
501 }
502
mdiSubWindow() const503 QMdiSubWindow *QAccessibleMdiSubWindow::mdiSubWindow() const
504 {
505 return static_cast<QMdiSubWindow *>(object());
506 }
507 #endif // QT_CONFIG(mdiarea)
508
509 #if QT_CONFIG(dialogbuttonbox)
510 // ======================= QAccessibleDialogButtonBox ======================
QAccessibleDialogButtonBox(QWidget * widget)511 QAccessibleDialogButtonBox::QAccessibleDialogButtonBox(QWidget *widget)
512 : QAccessibleWidget(widget, QAccessible::Grouping)
513 {
514 Q_ASSERT(qobject_cast<QDialogButtonBox*>(widget));
515 }
516
517 #endif // QT_CONFIG(dialogbuttonbox)
518
519 #if QT_CONFIG(textbrowser) && !defined(QT_NO_CURSOR)
QAccessibleTextBrowser(QWidget * widget)520 QAccessibleTextBrowser::QAccessibleTextBrowser(QWidget *widget)
521 : QAccessibleTextEdit(widget)
522 {
523 Q_ASSERT(qobject_cast<QTextBrowser *>(widget));
524 }
525
role() const526 QAccessible::Role QAccessibleTextBrowser::role() const
527 {
528 return QAccessible::StaticText;
529 }
530 #endif // QT_CONFIG(textbrowser) && QT_NO_CURSOR
531
532 #if QT_CONFIG(calendarwidget)
533 // ===================== QAccessibleCalendarWidget ========================
QAccessibleCalendarWidget(QWidget * widget)534 QAccessibleCalendarWidget::QAccessibleCalendarWidget(QWidget *widget)
535 : QAccessibleWidget(widget, QAccessible::Table)
536 {
537 Q_ASSERT(qobject_cast<QCalendarWidget *>(widget));
538 }
539
childCount() const540 int QAccessibleCalendarWidget::childCount() const
541 {
542 return calendarWidget()->isNavigationBarVisible() ? 2 : 1;
543 }
544
indexOfChild(const QAccessibleInterface * child) const545 int QAccessibleCalendarWidget::indexOfChild(const QAccessibleInterface *child) const
546 {
547 if (!child || !child->object() || childCount() <= 0)
548 return -1;
549 if (qobject_cast<QAbstractItemView *>(child->object()))
550 return childCount() - 1; // FIXME
551 return 0;
552 }
553
child(int index) const554 QAccessibleInterface *QAccessibleCalendarWidget::child(int index) const
555 {
556 if (index < 0 || index >= childCount())
557 return nullptr;
558
559 if (childCount() > 1 && index == 0)
560 return QAccessible::queryAccessibleInterface(navigationBar());
561
562 return QAccessible::queryAccessibleInterface(calendarView());
563 }
564
calendarWidget() const565 QCalendarWidget *QAccessibleCalendarWidget::calendarWidget() const
566 {
567 return static_cast<QCalendarWidget *>(object());
568 }
569
calendarView() const570 QAbstractItemView *QAccessibleCalendarWidget::calendarView() const
571 {
572 for (QObject *child : calendarWidget()->children()) {
573 if (child->objectName() == QLatin1String("qt_calendar_calendarview"))
574 return static_cast<QAbstractItemView *>(child);
575 }
576 return nullptr;
577 }
578
navigationBar() const579 QWidget *QAccessibleCalendarWidget::navigationBar() const
580 {
581 for (QObject *child : calendarWidget()->children()) {
582 if (child->objectName() == QLatin1String("qt_calendar_navigationbar"))
583 return static_cast<QWidget *>(child);
584 }
585 return nullptr;
586 }
587 #endif // QT_CONFIG(calendarwidget)
588
589 #if QT_CONFIG(dockwidget)
590
591 // Dock Widget - order of children:
592 // - Content widget
593 // - Float button
594 // - Close button
595 // If there is a custom title bar widget, that one becomes child 1, after the content 0
596 // (in that case the buttons are ignored)
QAccessibleDockWidget(QWidget * widget)597 QAccessibleDockWidget::QAccessibleDockWidget(QWidget *widget)
598 : QAccessibleWidget(widget, QAccessible::Window)
599 {
600 }
601
dockWidgetLayout() const602 QDockWidgetLayout *QAccessibleDockWidget::dockWidgetLayout() const
603 {
604 return qobject_cast<QDockWidgetLayout*>(dockWidget()->layout());
605 }
606
childCount() const607 int QAccessibleDockWidget::childCount() const
608 {
609 if (dockWidget()->titleBarWidget()) {
610 return dockWidget()->widget() ? 2 : 1;
611 }
612 return dockWidgetLayout()->count();
613 }
614
child(int index) const615 QAccessibleInterface *QAccessibleDockWidget::child(int index) const
616 {
617 if (dockWidget()->titleBarWidget()) {
618 if ((!dockWidget()->widget() && index == 0) || (index == 1))
619 return QAccessible::queryAccessibleInterface(dockWidget()->titleBarWidget());
620 if (index == 0)
621 return QAccessible::queryAccessibleInterface(dockWidget()->widget());
622 } else {
623 QLayoutItem *item = dockWidgetLayout()->itemAt(index);
624 if (item)
625 return QAccessible::queryAccessibleInterface(item->widget());
626 }
627 return nullptr;
628 }
629
indexOfChild(const QAccessibleInterface * child) const630 int QAccessibleDockWidget::indexOfChild(const QAccessibleInterface *child) const
631 {
632 if (!child || !child->object() || child->object()->parent() != object())
633 return -1;
634
635 if (dockWidget()->titleBarWidget() == child->object()) {
636 return dockWidget()->widget() ? 1 : 0;
637 }
638
639 return dockWidgetLayout()->indexOf(qobject_cast<QWidget*>(child->object()));
640 }
641
rect() const642 QRect QAccessibleDockWidget::rect() const
643 {
644 QRect rect;
645
646 if (dockWidget()->isFloating()) {
647 rect = dockWidget()->frameGeometry();
648 } else {
649 rect = dockWidget()->rect();
650 rect.moveTopLeft(dockWidget()->mapToGlobal(rect.topLeft()));
651 }
652
653 return rect;
654 }
655
dockWidget() const656 QDockWidget *QAccessibleDockWidget::dockWidget() const
657 {
658 return static_cast<QDockWidget *>(object());
659 }
660
text(QAccessible::Text t) const661 QString QAccessibleDockWidget::text(QAccessible::Text t) const
662 {
663 if (t == QAccessible::Name) {
664 return qt_accStripAmp(dockWidget()->windowTitle());
665 } else if (t == QAccessible::Accelerator) {
666 return qt_accHotKey(dockWidget()->windowTitle());
667 }
668 return QString();
669 }
670 #endif // QT_CONFIG(dockwidget)
671
672 #ifndef QT_NO_CURSOR
673
QAccessibleTextWidget(QWidget * o,QAccessible::Role r,const QString & name)674 QAccessibleTextWidget::QAccessibleTextWidget(QWidget *o, QAccessible::Role r, const QString &name):
675 QAccessibleWidget(o, r, name)
676 {
677
678 }
679
state() const680 QAccessible::State QAccessibleTextWidget::state() const
681 {
682 QAccessible::State s = QAccessibleWidget::state();
683 s.selectableText = true;
684 s.multiLine = true;
685 return s;
686 }
687
characterRect(int offset) const688 QRect QAccessibleTextWidget::characterRect(int offset) const
689 {
690 QTextBlock block = textDocument()->findBlock(offset);
691 if (!block.isValid())
692 return QRect();
693
694 QTextLayout *layout = block.layout();
695 QPointF layoutPosition = layout->position();
696 int relativeOffset = offset - block.position();
697 QTextLine line = layout->lineForTextPosition(relativeOffset);
698
699 QRect r;
700
701 if (line.isValid()) {
702 qreal x = line.cursorToX(relativeOffset);
703
704 QTextCharFormat format;
705 QTextBlock::iterator iter = block.begin();
706 if (iter.atEnd())
707 format = block.charFormat();
708 else {
709 while (!iter.atEnd() && !iter.fragment().contains(offset))
710 ++iter;
711 if (iter.atEnd()) // newline should have same format as preceding character
712 --iter;
713 format = iter.fragment().charFormat();
714 }
715
716 QFontMetrics fm(format.font());
717 const QString ch = text(offset, offset + 1);
718 if (!ch.isEmpty()) {
719 int w = fm.horizontalAdvance(ch);
720 int h = fm.height();
721 r = QRect(layoutPosition.x() + x, layoutPosition.y() + line.y() + line.ascent() + fm.descent() - h,
722 w, h);
723 r.moveTo(viewport()->mapToGlobal(r.topLeft()));
724 }
725 r.translate(-scrollBarPosition());
726 }
727
728 return r;
729 }
730
offsetAtPoint(const QPoint & point) const731 int QAccessibleTextWidget::offsetAtPoint(const QPoint &point) const
732 {
733 QPoint p = viewport()->mapFromGlobal(point);
734 // convert to document coordinates
735 p += scrollBarPosition();
736 return textDocument()->documentLayout()->hitTest(p, Qt::ExactHit);
737 }
738
selectionCount() const739 int QAccessibleTextWidget::selectionCount() const
740 {
741 return textCursor().hasSelection() ? 1 : 0;
742 }
743
744 namespace {
745 /*!
746 \internal
747 \brief Helper class for AttributeFormatter
748
749 This class is returned from AttributeFormatter's indexing operator to act
750 as a proxy for the following assignment.
751
752 It uses perfect forwarding in its assignment operator to amend the RHS
753 with the formatting of the key, using QStringBuilder. Consequently, the
754 RHS can be anything that QStringBuilder supports.
755 */
756 class AttributeFormatterRef {
757 QString &string;
758 const char *key;
759 friend class AttributeFormatter;
AttributeFormatterRef(QString & string,const char * key)760 AttributeFormatterRef(QString &string, const char *key) : string(string), key(key) {}
761 public:
762 template <typename RHS>
operator =(RHS && rhs)763 void operator=(RHS &&rhs)
764 { string += QLatin1String(key) + QLatin1Char(':') + std::forward<RHS>(rhs) + QLatin1Char(';'); }
765 };
766
767 /*!
768 \internal
769 \brief Small string-builder class that supports a map-like API to serialize key-value pairs.
770 \code
771 AttributeFormatter attrs;
772 attrs["foo"] = QLatinString("hello") + world + QLatin1Char('!');
773 \endcode
774 The key type is always \c{const char*}, and the right-hand-side can
775 be any QStringBuilder expression.
776
777 Breaking it down, this class provides the indexing operator, stores
778 the key in an instance of, and then returns, AttributeFormatterRef,
779 which is the class that provides the assignment part of the operation.
780 */
781 class AttributeFormatter {
782 QString string;
783 public:
operator [](const char * key)784 AttributeFormatterRef operator[](const char *key)
785 { return AttributeFormatterRef(string, key); }
786
toFormatted() const787 QString toFormatted() const { return string; }
788 };
789 } // unnamed namespace
790
attributes(int offset,int * startOffset,int * endOffset) const791 QString QAccessibleTextWidget::attributes(int offset, int *startOffset, int *endOffset) const
792 {
793 /* The list of attributes can be found at:
794 http://linuxfoundation.org/collaborate/workgroups/accessibility/iaccessible2/textattributes
795 */
796
797 // IAccessible2 defines -1 as length and -2 as cursor position
798 if (offset == -2)
799 offset = cursorPosition();
800
801 const int charCount = characterCount();
802
803 // -1 doesn't make much sense here, but it's better to return something
804 // screen readers may ask for text attributes at the cursor pos which may be equal to length
805 if (offset == -1 || offset == charCount)
806 offset = charCount - 1;
807
808 if (offset < 0 || offset > charCount) {
809 *startOffset = -1;
810 *endOffset = -1;
811 return QString();
812 }
813
814
815 QTextCursor cursor = textCursor();
816 cursor.setPosition(offset);
817 QTextBlock block = cursor.block();
818
819 int blockStart = block.position();
820 int blockEnd = blockStart + block.length();
821
822 QTextBlock::iterator iter = block.begin();
823 int lastFragmentIndex = blockStart;
824 while (!iter.atEnd()) {
825 QTextFragment f = iter.fragment();
826 if (f.contains(offset))
827 break;
828 lastFragmentIndex = f.position() + f.length();
829 ++iter;
830 }
831
832 QTextCharFormat charFormat;
833 if (!iter.atEnd()) {
834 QTextFragment fragment = iter.fragment();
835 charFormat = fragment.charFormat();
836 int pos = fragment.position();
837 // text block and fragment may overlap, use the smallest common range
838 *startOffset = qMax(pos, blockStart);
839 *endOffset = qMin(pos + fragment.length(), blockEnd);
840 } else {
841 charFormat = block.charFormat();
842 *startOffset = lastFragmentIndex;
843 *endOffset = blockEnd;
844 }
845 Q_ASSERT(*startOffset <= offset);
846 Q_ASSERT(*endOffset >= offset);
847
848 QTextBlockFormat blockFormat = cursor.blockFormat();
849
850 const QFont charFormatFont = charFormat.font();
851
852 AttributeFormatter attrs;
853 QString family = charFormatFont.family();
854 if (!family.isEmpty()) {
855 family = family.replace('\\', QLatin1String("\\\\"));
856 family = family.replace(':', QLatin1String("\\:"));
857 family = family.replace(',', QLatin1String("\\,"));
858 family = family.replace('=', QLatin1String("\\="));
859 family = family.replace(';', QLatin1String("\\;"));
860 family = family.replace('\"', QLatin1String("\\\""));
861 attrs["font-family"] = QLatin1Char('"') + family + QLatin1Char('"');
862 }
863
864 int fontSize = int(charFormatFont.pointSize());
865 if (fontSize)
866 attrs["font-size"] = QString::fromLatin1("%1pt").arg(fontSize);
867
868 //Different weight values are not handled
869 attrs["font-weight"] = QString::fromLatin1(charFormatFont.weight() > QFont::Normal ? "bold" : "normal");
870
871 QFont::Style style = charFormatFont.style();
872 attrs["font-style"] = QString::fromLatin1((style == QFont::StyleItalic) ? "italic" : ((style == QFont::StyleOblique) ? "oblique": "normal"));
873
874 QTextCharFormat::UnderlineStyle underlineStyle = charFormat.underlineStyle();
875 if (underlineStyle == QTextCharFormat::NoUnderline && charFormatFont.underline()) // underline could still be set in the default font
876 underlineStyle = QTextCharFormat::SingleUnderline;
877 QString underlineStyleValue;
878 switch (underlineStyle) {
879 case QTextCharFormat::NoUnderline:
880 break;
881 case QTextCharFormat::SingleUnderline:
882 underlineStyleValue = QStringLiteral("solid");
883 break;
884 case QTextCharFormat::DashUnderline:
885 underlineStyleValue = QStringLiteral("dash");
886 break;
887 case QTextCharFormat::DotLine:
888 underlineStyleValue = QStringLiteral("dash");
889 break;
890 case QTextCharFormat::DashDotLine:
891 underlineStyleValue = QStringLiteral("dot-dash");
892 break;
893 case QTextCharFormat::DashDotDotLine:
894 underlineStyleValue = QStringLiteral("dot-dot-dash");
895 break;
896 case QTextCharFormat::WaveUnderline:
897 underlineStyleValue = QStringLiteral("wave");
898 break;
899 case QTextCharFormat::SpellCheckUnderline:
900 underlineStyleValue = QStringLiteral("wave"); // this is not correct, but provides good approximation at least
901 break;
902 default:
903 qWarning() << "Unknown QTextCharFormat::UnderlineStyle value " << underlineStyle << " could not be translated to IAccessible2 value";
904 break;
905 }
906 if (!underlineStyleValue.isNull()) {
907 attrs["text-underline-style"] = underlineStyleValue;
908 attrs["text-underline-type"] = QStringLiteral("single"); // if underlineStyleValue is set, there is an underline, and Qt does not support other than single ones
909 } // else both are "none" which is the default - no need to set them
910
911 if (block.textDirection() == Qt::RightToLeft)
912 attrs["writing-mode"] = QStringLiteral("rl");
913
914 QTextCharFormat::VerticalAlignment alignment = charFormat.verticalAlignment();
915 attrs["text-position"] = QString::fromLatin1((alignment == QTextCharFormat::AlignSubScript) ? "sub" : ((alignment == QTextCharFormat::AlignSuperScript) ? "super" : "baseline" ));
916
917 QBrush background = charFormat.background();
918 if (background.style() == Qt::SolidPattern) {
919 attrs["background-color"] = QString::fromLatin1("rgb(%1,%2,%3)").arg(background.color().red()).arg(background.color().green()).arg(background.color().blue());
920 }
921
922 QBrush foreground = charFormat.foreground();
923 if (foreground.style() == Qt::SolidPattern) {
924 attrs["color"] = QString::fromLatin1("rgb(%1,%2,%3)").arg(foreground.color().red()).arg(foreground.color().green()).arg(foreground.color().blue());
925 }
926
927 switch (blockFormat.alignment() & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter | Qt::AlignJustify)) {
928 case Qt::AlignLeft:
929 attrs["text-align"] = QStringLiteral("left");
930 break;
931 case Qt::AlignRight:
932 attrs["text-align"] = QStringLiteral("right");
933 break;
934 case Qt::AlignHCenter:
935 attrs["text-align"] = QStringLiteral("center");
936 break;
937 case Qt::AlignJustify:
938 attrs["text-align"] = QStringLiteral("justify");
939 break;
940 }
941
942 return attrs.toFormatted();
943 }
944
cursorPosition() const945 int QAccessibleTextWidget::cursorPosition() const
946 {
947 return textCursor().position();
948 }
949
selection(int selectionIndex,int * startOffset,int * endOffset) const950 void QAccessibleTextWidget::selection(int selectionIndex, int *startOffset, int *endOffset) const
951 {
952 *startOffset = *endOffset = 0;
953 QTextCursor cursor = textCursor();
954
955 if (selectionIndex != 0 || !cursor.hasSelection())
956 return;
957
958 *startOffset = cursor.selectionStart();
959 *endOffset = cursor.selectionEnd();
960 }
961
text(int startOffset,int endOffset) const962 QString QAccessibleTextWidget::text(int startOffset, int endOffset) const
963 {
964 QTextCursor cursor(textCursor());
965
966 cursor.setPosition(startOffset, QTextCursor::MoveAnchor);
967 cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
968
969 return cursor.selectedText().replace(QChar(QChar::ParagraphSeparator), QLatin1Char('\n'));
970 }
971
scrollBarPosition() const972 QPoint QAccessibleTextWidget::scrollBarPosition() const
973 {
974 return QPoint(0, 0);
975 }
976
977
textBeforeOffset(int offset,QAccessible::TextBoundaryType boundaryType,int * startOffset,int * endOffset) const978 QString QAccessibleTextWidget::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType,
979 int *startOffset, int *endOffset) const
980 {
981 Q_ASSERT(startOffset);
982 Q_ASSERT(endOffset);
983
984 QTextCursor cursor = textCursor();
985 cursor.setPosition(offset);
986 QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
987 cursor.setPosition(boundaries.first - 1);
988 boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
989
990 *startOffset = boundaries.first;
991 *endOffset = boundaries.second;
992
993 return text(boundaries.first, boundaries.second);
994 }
995
996
textAfterOffset(int offset,QAccessible::TextBoundaryType boundaryType,int * startOffset,int * endOffset) const997 QString QAccessibleTextWidget::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType,
998 int *startOffset, int *endOffset) const
999 {
1000 Q_ASSERT(startOffset);
1001 Q_ASSERT(endOffset);
1002
1003 QTextCursor cursor = textCursor();
1004 cursor.setPosition(offset);
1005 QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
1006 cursor.setPosition(boundaries.second);
1007 boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
1008
1009 *startOffset = boundaries.first;
1010 *endOffset = boundaries.second;
1011
1012 return text(boundaries.first, boundaries.second);
1013 }
1014
textAtOffset(int offset,QAccessible::TextBoundaryType boundaryType,int * startOffset,int * endOffset) const1015 QString QAccessibleTextWidget::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
1016 int *startOffset, int *endOffset) const
1017 {
1018 Q_ASSERT(startOffset);
1019 Q_ASSERT(endOffset);
1020
1021 QTextCursor cursor = textCursor();
1022 cursor.setPosition(offset);
1023 QPair<int, int> boundaries = QAccessible::qAccessibleTextBoundaryHelper(cursor, boundaryType);
1024
1025 *startOffset = boundaries.first;
1026 *endOffset = boundaries.second;
1027
1028 return text(boundaries.first, boundaries.second);
1029 }
1030
setCursorPosition(int position)1031 void QAccessibleTextWidget::setCursorPosition(int position)
1032 {
1033 QTextCursor cursor = textCursor();
1034 cursor.setPosition(position);
1035 setTextCursor(cursor);
1036 }
1037
addSelection(int startOffset,int endOffset)1038 void QAccessibleTextWidget::addSelection(int startOffset, int endOffset)
1039 {
1040 setSelection(0, startOffset, endOffset);
1041 }
1042
removeSelection(int selectionIndex)1043 void QAccessibleTextWidget::removeSelection(int selectionIndex)
1044 {
1045 if (selectionIndex != 0)
1046 return;
1047
1048 QTextCursor cursor = textCursor();
1049 cursor.clearSelection();
1050 setTextCursor(cursor);
1051 }
1052
setSelection(int selectionIndex,int startOffset,int endOffset)1053 void QAccessibleTextWidget::setSelection(int selectionIndex, int startOffset, int endOffset)
1054 {
1055 if (selectionIndex != 0)
1056 return;
1057
1058 QTextCursor cursor = textCursor();
1059 cursor.setPosition(startOffset, QTextCursor::MoveAnchor);
1060 cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
1061 setTextCursor(cursor);
1062 }
1063
characterCount() const1064 int QAccessibleTextWidget::characterCount() const
1065 {
1066 QTextCursor cursor = textCursor();
1067 cursor.movePosition(QTextCursor::End);
1068 return cursor.position();
1069 }
1070
textCursorForRange(int startOffset,int endOffset) const1071 QTextCursor QAccessibleTextWidget::textCursorForRange(int startOffset, int endOffset) const
1072 {
1073 QTextCursor cursor = textCursor();
1074 cursor.setPosition(startOffset, QTextCursor::MoveAnchor);
1075 cursor.setPosition(endOffset, QTextCursor::KeepAnchor);
1076
1077 return cursor;
1078 }
1079
deleteText(int startOffset,int endOffset)1080 void QAccessibleTextWidget::deleteText(int startOffset, int endOffset)
1081 {
1082 QTextCursor cursor = textCursorForRange(startOffset, endOffset);
1083 cursor.removeSelectedText();
1084 }
1085
insertText(int offset,const QString & text)1086 void QAccessibleTextWidget::insertText(int offset, const QString &text)
1087 {
1088 QTextCursor cursor = textCursor();
1089 cursor.setPosition(offset);
1090 cursor.insertText(text);
1091 }
1092
replaceText(int startOffset,int endOffset,const QString & text)1093 void QAccessibleTextWidget::replaceText(int startOffset, int endOffset, const QString &text)
1094 {
1095 QTextCursor cursor = textCursorForRange(startOffset, endOffset);
1096 cursor.removeSelectedText();
1097 cursor.insertText(text);
1098 }
1099 #endif // QT_NO_CURSOR
1100
1101
1102 #if QT_CONFIG(mainwindow)
QAccessibleMainWindow(QWidget * widget)1103 QAccessibleMainWindow::QAccessibleMainWindow(QWidget *widget)
1104 : QAccessibleWidget(widget, QAccessible::Window) { }
1105
child(int index) const1106 QAccessibleInterface *QAccessibleMainWindow::child(int index) const
1107 {
1108 QList<QWidget*> kids = childWidgets(mainWindow());
1109 if (index >= 0 && index < kids.count()) {
1110 return QAccessible::queryAccessibleInterface(kids.at(index));
1111 }
1112 return nullptr;
1113 }
1114
childCount() const1115 int QAccessibleMainWindow::childCount() const
1116 {
1117 QList<QWidget*> kids = childWidgets(mainWindow());
1118 return kids.count();
1119 }
1120
indexOfChild(const QAccessibleInterface * iface) const1121 int QAccessibleMainWindow::indexOfChild(const QAccessibleInterface *iface) const
1122 {
1123 QList<QWidget*> kids = childWidgets(mainWindow());
1124 return kids.indexOf(static_cast<QWidget*>(iface->object()));
1125 }
1126
childAt(int x,int y) const1127 QAccessibleInterface *QAccessibleMainWindow::childAt(int x, int y) const
1128 {
1129 QWidget *w = widget();
1130 if (!w->isVisible())
1131 return nullptr;
1132 QPoint gp = w->mapToGlobal(QPoint(0, 0));
1133 if (!QRect(gp.x(), gp.y(), w->width(), w->height()).contains(x, y))
1134 return nullptr;
1135
1136 const QWidgetList kids = childWidgets(mainWindow());
1137 QPoint rp = mainWindow()->mapFromGlobal(QPoint(x, y));
1138 for (QWidget *child : kids) {
1139 if (!child->isWindow() && !child->isHidden() && child->geometry().contains(rp)) {
1140 return QAccessible::queryAccessibleInterface(child);
1141 }
1142 }
1143 return nullptr;
1144 }
1145
mainWindow() const1146 QMainWindow *QAccessibleMainWindow::mainWindow() const
1147 {
1148 return qobject_cast<QMainWindow *>(object());
1149 }
1150
1151 #endif // QT_CONFIG(mainwindow)
1152
1153 QT_END_NAMESPACE
1154
1155 #endif // QT_NO_ACCESSIBILITY
1156