1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "itemviews_p.h"
5
6#include <qheaderview.h>
7#if QT_CONFIG(tableview)
8#include <qtableview.h>
9#endif
10#if QT_CONFIG(listview)
11#include <qlistview.h>
12#endif
13#if QT_CONFIG(treeview)
14#include <qtreeview.h>
15#include <private/qtreeview_p.h>
16#endif
17#include <private/qwidget_p.h>
18
19#if QT_CONFIG(accessibility)
20
21QT_BEGIN_NAMESPACE
22
23/*
24Implementation of the IAccessible2 table2 interface. Much simpler than
25the other table interfaces since there is only the main table and cells:
26
27TABLE/LIST/TREE
28 |- HEADER CELL
29 |- CELL
30 |- CELL
31 ...
32*/
33
34
35QAbstractItemView *QAccessibleTable::view() const
36{
37 return qobject_cast<QAbstractItemView*>(object: object());
38}
39
40int QAccessibleTable::logicalIndex(const QModelIndex &index) const
41{
42 const QAbstractItemModel *theModel = index.model();
43 if (!theModel || !index.isValid())
44 return -1;
45
46#if QT_CONFIG(listview)
47 if (m_role == QAccessible::List) {
48 if (index.column() != qobject_cast<const QListView*>(object: view())->modelColumn())
49 return -1;
50 else
51 return index.row();
52 } else
53#endif
54 {
55 const int vHeader = verticalHeader() ? 1 : 0;
56 const int hHeader = horizontalHeader() ? 1 : 0;
57 return (index.row() + hHeader) * (columnCount() + vHeader)
58 + (index.column() + vHeader);
59 }
60}
61
62QAccessibleTable::QAccessibleTable(QWidget *w)
63 : QAccessibleObject(w)
64{
65 Q_ASSERT(view());
66
67#if QT_CONFIG(tableview)
68 if (qobject_cast<const QTableView*>(object: view())) {
69 m_role = QAccessible::Table;
70 } else
71#endif
72#if QT_CONFIG(treeview)
73 if (qobject_cast<const QTreeView*>(object: view())) {
74 m_role = QAccessible::Tree;
75 } else
76#endif
77#if QT_CONFIG(listview)
78 if (qobject_cast<const QListView*>(object: view())) {
79 m_role = QAccessible::List;
80 } else
81#endif
82 {
83 // is this our best guess?
84 m_role = QAccessible::Table;
85 }
86}
87
88bool QAccessibleTable::isValid() const
89{
90 return view() && !qt_widget_private(widget: view())->data.in_destructor;
91}
92
93QAccessibleTable::~QAccessibleTable()
94{
95 for (QAccessible::Id id : std::as_const(t&: childToId))
96 QAccessible::deleteAccessibleInterface(uniqueId: id);
97}
98
99QHeaderView *QAccessibleTable::horizontalHeader() const
100{
101 QHeaderView *header = nullptr;
102 if (false) {
103#if QT_CONFIG(tableview)
104 } else if (const QTableView *tv = qobject_cast<const QTableView*>(object: view())) {
105 header = tv->horizontalHeader();
106#endif
107#if QT_CONFIG(treeview)
108 } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(object: view())) {
109 header = tv->header();
110#endif
111 }
112 return header;
113}
114
115QHeaderView *QAccessibleTable::verticalHeader() const
116{
117 QHeaderView *header = nullptr;
118 if (false) {
119#if QT_CONFIG(tableview)
120 } else if (const QTableView *tv = qobject_cast<const QTableView*>(object: view())) {
121 header = tv->verticalHeader();
122#endif
123 }
124 return header;
125}
126
127QAccessibleInterface *QAccessibleTable::cellAt(int row, int column) const
128{
129 const QAbstractItemView *theView = view();
130 const QAbstractItemModel *theModel = theView->model();
131 if (!theModel)
132 return nullptr;
133 Q_ASSERT(role() != QAccessible::List);
134 Q_ASSERT(role() != QAccessible::Tree);
135 QModelIndex index = theModel->index(row, column, parent: theView->rootIndex());
136 if (Q_UNLIKELY(!index.isValid())) {
137 qWarning() << "QAccessibleTable::cellAt: invalid index: " << index << " for " << theView;
138 return nullptr;
139 }
140 return child(index: logicalIndex(index));
141}
142
143QAccessibleInterface *QAccessibleTable::caption() const
144{
145 return nullptr;
146}
147
148QString QAccessibleTable::columnDescription(int column) const
149{
150 const QAbstractItemView *theView = view();
151 const QAbstractItemModel *theModel = theView->model();
152 if (!theModel)
153 return QString();
154 return theModel->headerData(section: column, orientation: Qt::Horizontal).toString();
155}
156
157int QAccessibleTable::columnCount() const
158{
159 const QAbstractItemView *theView = view();
160 const QAbstractItemModel *theModel = theView->model();
161 if (!theModel)
162 return 0;
163 const int modelColumnCount = theModel->columnCount(parent: theView->rootIndex());
164 return m_role == QAccessible::List ? qMin(a: 1, b: modelColumnCount) : modelColumnCount;
165}
166
167int QAccessibleTable::rowCount() const
168{
169 const QAbstractItemView *theView = view();
170 const QAbstractItemModel *theModel = theView->model();
171 if (!theModel)
172 return 0;
173 return theModel->rowCount(parent: theView->rootIndex());
174}
175
176int QAccessibleTable::selectedCellCount() const
177{
178 if (!view()->selectionModel())
179 return 0;
180 return view()->selectionModel()->selectedIndexes().size();
181}
182
183int QAccessibleTable::selectedColumnCount() const
184{
185 if (!view()->selectionModel())
186 return 0;
187 return view()->selectionModel()->selectedColumns().size();
188}
189
190int QAccessibleTable::selectedRowCount() const
191{
192 if (!view()->selectionModel())
193 return 0;
194 return view()->selectionModel()->selectedRows().size();
195}
196
197QString QAccessibleTable::rowDescription(int row) const
198{
199 const QAbstractItemView *theView = view();
200 const QAbstractItemModel *theModel = theView->model();
201 if (!theModel)
202 return QString();
203 return theModel->headerData(section: row, orientation: Qt::Vertical).toString();
204}
205
206QList<QAccessibleInterface *> QAccessibleTable::selectedCells() const
207{
208 QList<QAccessibleInterface*> cells;
209 if (!view()->selectionModel())
210 return cells;
211 const QModelIndexList selectedIndexes = view()->selectionModel()->selectedIndexes();
212 cells.reserve(asize: selectedIndexes.size());
213 for (const QModelIndex &index : selectedIndexes)
214 cells.append(t: child(index: logicalIndex(index)));
215 return cells;
216}
217
218QList<int> QAccessibleTable::selectedColumns() const
219{
220 if (!view()->selectionModel())
221 return QList<int>();
222 QList<int> columns;
223 const QModelIndexList selectedColumns = view()->selectionModel()->selectedColumns();
224 columns.reserve(asize: selectedColumns.size());
225 for (const QModelIndex &index : selectedColumns)
226 columns.append(t: index.column());
227
228 return columns;
229}
230
231QList<int> QAccessibleTable::selectedRows() const
232{
233 if (!view()->selectionModel())
234 return QList<int>();
235 QList<int> rows;
236 const QModelIndexList selectedRows = view()->selectionModel()->selectedRows();
237 rows.reserve(asize: selectedRows.size());
238 for (const QModelIndex &index : selectedRows)
239 rows.append(t: index.row());
240
241 return rows;
242}
243
244QAccessibleInterface *QAccessibleTable::summary() const
245{
246 return nullptr;
247}
248
249bool QAccessibleTable::isColumnSelected(int column) const
250{
251 if (!view()->selectionModel())
252 return false;
253 return view()->selectionModel()->isColumnSelected(column, parent: QModelIndex());
254}
255
256bool QAccessibleTable::isRowSelected(int row) const
257{
258 if (!view()->selectionModel())
259 return false;
260 return view()->selectionModel()->isRowSelected(row, parent: QModelIndex());
261}
262
263bool QAccessibleTable::selectRow(int row)
264{
265 QAbstractItemView *theView = view();
266 const QAbstractItemModel *theModel = theView->model();
267 if (!theModel || !view()->selectionModel())
268 return false;
269
270 const QModelIndex rootIndex = theView->rootIndex();
271 const QModelIndex index = theModel->index(row, column: 0, parent: rootIndex);
272
273 if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns)
274 return false;
275
276 switch (view()->selectionMode()) {
277 case QAbstractItemView::NoSelection:
278 return false;
279 case QAbstractItemView::SingleSelection:
280 if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 )
281 return false;
282 view()->clearSelection();
283 break;
284 case QAbstractItemView::ContiguousSelection:
285 if ((!row || !theView->selectionModel()->isRowSelected(row: row - 1, parent: rootIndex))
286 && !theView->selectionModel()->isRowSelected(row: row + 1, parent: rootIndex)) {
287 theView->clearSelection();
288 }
289 break;
290 default:
291 break;
292 }
293
294 view()->selectionModel()->select(index, command: QItemSelectionModel::Select | QItemSelectionModel::Rows);
295 return true;
296}
297
298bool QAccessibleTable::selectColumn(int column)
299{
300 QAbstractItemView *theView = view();
301 const QAbstractItemModel *theModel = theView->model();
302 auto *selectionModel = theView->selectionModel();
303 if (!theModel || !selectionModel)
304 return false;
305
306 const QModelIndex rootIndex = theView->rootIndex();
307 const QModelIndex index = theModel->index(row: 0, column, parent: rootIndex);
308
309 if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectRows)
310 return false;
311
312 switch (theView->selectionMode()) {
313 case QAbstractItemView::NoSelection:
314 return false;
315 case QAbstractItemView::SingleSelection:
316 if (theView->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1)
317 return false;
318 Q_FALLTHROUGH();
319 case QAbstractItemView::ContiguousSelection:
320 if ((!column || !selectionModel->isColumnSelected(column: column - 1, parent: rootIndex))
321 && !selectionModel->isColumnSelected(column: column + 1, parent: rootIndex)) {
322 theView->clearSelection();
323 }
324 break;
325 default:
326 break;
327 }
328
329 selectionModel->select(index, command: QItemSelectionModel::Select | QItemSelectionModel::Columns);
330 return true;
331}
332
333bool QAccessibleTable::unselectRow(int row)
334{
335 const QAbstractItemView *theView = view();
336 const QAbstractItemModel *theModel = theView->model();
337 auto *selectionModel = theView->selectionModel();
338 if (!theModel || !selectionModel)
339 return false;
340
341 const QModelIndex rootIndex = theView->rootIndex();
342 const QModelIndex index = view()->model()->index(row, column: 0, parent: rootIndex);
343 if (!index.isValid())
344 return false;
345
346 QItemSelection selection(index, index);
347
348 switch (theView->selectionMode()) {
349 case QAbstractItemView::SingleSelection:
350 //In SingleSelection and ContiguousSelection once an item
351 //is selected, there's no way for the user to unselect all items
352 if (selectedRowCount() == 1)
353 return false;
354 break;
355 case QAbstractItemView::ContiguousSelection:
356 if (selectedRowCount() == 1)
357 return false;
358
359 if ((!row || selectionModel->isRowSelected(row: row - 1, parent: rootIndex))
360 && selectionModel->isRowSelected(row: row + 1, parent: rootIndex)) {
361 //If there are rows selected both up the current row and down the current rown,
362 //the ones which are down the current row will be deselected
363 selection = QItemSelection(index, theModel->index(row: rowCount() - 1, column: 0, parent: rootIndex));
364 }
365 break;
366 default:
367 break;
368 }
369
370 selectionModel->select(selection, command: QItemSelectionModel::Deselect | QItemSelectionModel::Rows);
371 return true;
372}
373
374bool QAccessibleTable::unselectColumn(int column)
375{
376 const QAbstractItemView *theView = view();
377 const QAbstractItemModel *theModel = theView->model();
378 auto *selectionModel = theView->selectionModel();
379 if (!theModel || !selectionModel)
380 return false;
381
382 const QModelIndex rootIndex = theView->rootIndex();
383 const QModelIndex index = view()->model()->index(row: 0, column, parent: rootIndex);
384 if (!index.isValid())
385 return false;
386
387 QItemSelection selection(index, index);
388
389 switch (view()->selectionMode()) {
390 case QAbstractItemView::SingleSelection:
391 //In SingleSelection and ContiguousSelection once an item
392 //is selected, there's no way for the user to unselect all items
393 if (selectedColumnCount() == 1)
394 return false;
395 break;
396 case QAbstractItemView::ContiguousSelection:
397 if (selectedColumnCount() == 1)
398 return false;
399
400 if ((!column || selectionModel->isColumnSelected(column: column - 1, parent: rootIndex))
401 && selectionModel->isColumnSelected(column: column + 1, parent: rootIndex)) {
402 //If there are columns selected both at the left of the current row and at the right
403 //of the current rown, the ones which are at the right will be deselected
404 selection = QItemSelection(index, theModel->index(row: 0, column: columnCount() - 1, parent: rootIndex));
405 }
406 break;
407 default:
408 break;
409 }
410
411 selectionModel->select(selection, command: QItemSelectionModel::Deselect | QItemSelectionModel::Columns);
412 return true;
413}
414
415int QAccessibleTable::selectedItemCount() const
416{
417 return selectedCellCount();
418}
419
420QList<QAccessibleInterface*> QAccessibleTable::selectedItems() const
421{
422 return selectedCells();
423}
424
425bool QAccessibleTable::isSelected(QAccessibleInterface *childCell) const
426{
427 if (!childCell || childCell->parent() != this) {
428 qWarning() << "QAccessibleTable::isSelected: Accessible interface must be a direct child of the table interface.";
429 return false;
430 }
431
432 const QAccessibleTableCellInterface *cell = childCell->tableCellInterface();
433 if (cell)
434 return cell->isSelected();
435
436 return false;
437}
438
439bool QAccessibleTable::select(QAccessibleInterface *childCell)
440{
441 if (!childCell || childCell->parent() != this) {
442 qWarning() << "QAccessibleTable::select: Accessible interface must be a direct child of the table interface.";
443 return false;
444 }
445
446 if (!childCell->tableCellInterface()) {
447 qWarning() << "QAccessibleTable::select: Accessible interface doesn't implement table cell interface.";
448 return false;
449 }
450
451 if (childCell->role() == QAccessible::Cell || childCell->role() == QAccessible::ListItem || childCell->role() == QAccessible::TreeItem) {
452 QAccessibleTableCell* cell = static_cast<QAccessibleTableCell*>(childCell);
453 cell->selectCell();
454 return true;
455 }
456
457 return false;
458}
459
460bool QAccessibleTable::unselect(QAccessibleInterface *childCell)
461{
462 if (!childCell || childCell->parent() != this) {
463 qWarning() << "QAccessibleTable::select: Accessible interface must be a direct child of the table interface.";
464 return false;
465 }
466
467 if (!childCell->tableCellInterface()) {
468 qWarning() << "QAccessibleTable::unselect: Accessible interface doesn't implement table cell interface.";
469 return false;
470 }
471
472 if (childCell->role() == QAccessible::Cell || childCell->role() == QAccessible::ListItem || childCell->role() == QAccessible::TreeItem) {
473 QAccessibleTableCell* cell = static_cast<QAccessibleTableCell*>(childCell);
474 cell->unselectCell();
475 return true;
476 }
477
478 return false;
479}
480
481bool QAccessibleTable::selectAll()
482{
483 view()->selectAll();
484 return true;
485}
486
487bool QAccessibleTable::clear()
488{
489 view()->selectionModel()->clearSelection();
490 return true;
491}
492
493
494QAccessible::Role QAccessibleTable::role() const
495{
496 return m_role;
497}
498
499QAccessible::State QAccessibleTable::state() const
500{
501 QAccessible::State state;
502 const auto *w = view();
503
504 if (w->testAttribute(attribute: Qt::WA_WState_Visible) == false)
505 state.invisible = true;
506 if (w->focusPolicy() != Qt::NoFocus)
507 state.focusable = true;
508 if (w->hasFocus())
509 state.focused = true;
510 if (!w->isEnabled())
511 state.disabled = true;
512 if (w->isWindow()) {
513 if (w->windowFlags() & Qt::WindowSystemMenuHint)
514 state.movable = true;
515 if (w->minimumSize() != w->maximumSize())
516 state.sizeable = true;
517 if (w->isActiveWindow())
518 state.active = true;
519 }
520
521 return state;
522}
523
524QAccessibleInterface *QAccessibleTable::childAt(int x, int y) const
525{
526 QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0));
527 QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset);
528 // FIXME: if indexPosition < 0 in one coordinate, return header
529
530 const QModelIndex index = view()->indexAt(point: indexPosition);
531 if (index.isValid())
532 return child(index: logicalIndex(index));
533 return nullptr;
534}
535
536QAccessibleInterface *QAccessibleTable::focusChild() const
537{
538 QModelIndex index = view()->currentIndex();
539 if (!index.isValid())
540 return nullptr;
541
542 return child(index: logicalIndex(index));
543}
544
545int QAccessibleTable::childCount() const
546{
547 const QAbstractItemView *theView = view();
548 if (!theView)
549 return 0;
550 const QAbstractItemModel *theModel = theView->model();
551 if (!theModel)
552 return 0;
553 const QModelIndex rootIndex = theView->rootIndex();
554 int vHeader = verticalHeader() ? 1 : 0;
555 int hHeader = horizontalHeader() ? 1 : 0;
556 return (theModel->rowCount(parent: rootIndex) + hHeader) * (columnCount() + vHeader);
557}
558
559int QAccessibleTable::indexOfChild(const QAccessibleInterface *iface) const
560{
561 const QAbstractItemView *theView = view();
562 if (!theView)
563 return -1;
564 const QAbstractItemModel *theModel = theView->model();
565 if (!theModel)
566 return -1;
567 QAccessibleInterface *parent = iface->parent();
568 if (parent->object() != theView)
569 return -1;
570
571 const QModelIndex rootIndex = theView->rootIndex();
572 Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class
573 if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) {
574 const QAccessibleTableCell* cell = static_cast<const QAccessibleTableCell*>(iface);
575 return logicalIndex(index: cell->m_index);
576 } else if (iface->role() == QAccessible::ColumnHeader){
577 const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface);
578 return cell->index + (verticalHeader() ? 1 : 0);
579 } else if (iface->role() == QAccessible::RowHeader){
580 const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface);
581 return (cell->index + 1) * (theModel->columnCount(parent: rootIndex) + 1);
582 } else if (iface->role() == QAccessible::Pane) {
583 return 0; // corner button
584 } else {
585 qWarning() << "WARNING QAccessibleTable::indexOfChild Fix my children..."
586 << iface->role() << iface->text(t: QAccessible::Name);
587 }
588 // FIXME: we are in denial of our children. this should stop.
589 return -1;
590}
591
592QString QAccessibleTable::text(QAccessible::Text t) const
593{
594 if (t == QAccessible::Description)
595 return view()->accessibleDescription();
596 return view()->accessibleName();
597}
598
599QRect QAccessibleTable::rect() const
600{
601 if (!view()->isVisible())
602 return QRect();
603 QPoint pos = view()->mapToGlobal(QPoint(0, 0));
604 return QRect(pos.x(), pos.y(), view()->width(), view()->height());
605}
606
607QAccessibleInterface *QAccessibleTable::parent() const
608{
609 if (view() && view()->parent()) {
610 if (qstrcmp(str1: "QComboBoxPrivateContainer", str2: view()->parent()->metaObject()->className()) == 0) {
611 return QAccessible::queryAccessibleInterface(view()->parent()->parent());
612 }
613 return QAccessible::queryAccessibleInterface(view()->parent());
614 }
615 return QAccessible::queryAccessibleInterface(qApp);
616}
617
618QAccessibleInterface *QAccessibleTable::child(int logicalIndex) const
619{
620 QAbstractItemView *theView = view();
621 if (!theView)
622 return nullptr;
623
624 const QAbstractItemModel *theModel = theView->model();
625 if (!theModel)
626 return nullptr;
627
628 const QModelIndex rootIndex = theView->rootIndex();
629 auto id = childToId.constFind(key: logicalIndex);
630 if (id != childToId.constEnd())
631 return QAccessible::accessibleInterface(uniqueId: id.value());
632
633 int vHeader = verticalHeader() ? 1 : 0;
634 int hHeader = horizontalHeader() ? 1 : 0;
635
636 int columns = theModel->columnCount(parent: rootIndex) + vHeader;
637
638 int row = logicalIndex / columns;
639 int column = logicalIndex % columns;
640
641 QAccessibleInterface *iface = nullptr;
642
643 if (vHeader) {
644 if (column == 0) {
645 if (hHeader && row == 0) {
646 iface = new QAccessibleTableCornerButton(theView);
647 } else {
648 iface = new QAccessibleTableHeaderCell(theView, row - hHeader, Qt::Vertical);
649 }
650 }
651 --column;
652 }
653 if (!iface && hHeader) {
654 if (row == 0) {
655 iface = new QAccessibleTableHeaderCell(theView, column, Qt::Horizontal);
656 }
657 --row;
658 }
659
660 if (!iface) {
661 QModelIndex index = theModel->index(row, column, parent: rootIndex);
662 if (Q_UNLIKELY(!index.isValid())) {
663 qWarning(msg: "QAccessibleTable::child: Invalid index at: %d %d", row, column);
664 return nullptr;
665 }
666 iface = new QAccessibleTableCell(theView, index, cellRole());
667 }
668
669 QAccessible::registerAccessibleInterface(iface);
670 childToId.insert(key: logicalIndex, value: QAccessible::uniqueId(iface));
671 return iface;
672}
673
674void *QAccessibleTable::interface_cast(QAccessible::InterfaceType t)
675{
676 if (t == QAccessible::SelectionInterface)
677 return static_cast<QAccessibleSelectionInterface*>(this);
678 if (t == QAccessible::TableInterface)
679 return static_cast<QAccessibleTableInterface*>(this);
680 return nullptr;
681}
682
683void QAccessibleTable::modelChange(QAccessibleTableModelChangeEvent *event)
684{
685 // if there is no cache yet, we don't update anything
686 if (childToId.isEmpty())
687 return;
688
689 switch (event->modelChangeType()) {
690 case QAccessibleTableModelChangeEvent::ModelReset:
691 for (QAccessible::Id id : std::as_const(t&: childToId))
692 QAccessible::deleteAccessibleInterface(uniqueId: id);
693 childToId.clear();
694 break;
695
696 // rows are inserted: move every row after that
697 case QAccessibleTableModelChangeEvent::RowsInserted:
698 case QAccessibleTableModelChangeEvent::ColumnsInserted: {
699 int newRows = event->lastRow() - event->firstRow() + 1;
700 int newColumns = event->lastColumn() - event->firstColumn() + 1;
701
702 ChildCache newCache;
703 ChildCache::ConstIterator iter = childToId.constBegin();
704
705 while (iter != childToId.constEnd()) {
706 QAccessible::Id id = iter.value();
707 QAccessibleInterface *iface = QAccessible::accessibleInterface(uniqueId: id);
708 Q_ASSERT(iface);
709 if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsInserted
710 && iface->role() == QAccessible::RowHeader) {
711 QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface);
712 if (cell->index >= event->firstRow()) {
713 cell->index += newRows;
714 }
715 } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsInserted
716 && iface->role() == QAccessible::ColumnHeader) {
717 QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface);
718 if (cell->index >= event->firstColumn()) {
719 cell->index += newColumns;
720 }
721 }
722 if (indexOfChild(iface) >= 0) {
723 newCache.insert(key: indexOfChild(iface), value: id);
724 } else {
725 // ### This should really not happen,
726 // but it might if the view has a root index set.
727 // This needs to be fixed.
728 QAccessible::deleteAccessibleInterface(uniqueId: id);
729 }
730 ++iter;
731 }
732 childToId = newCache;
733 break;
734 }
735
736 case QAccessibleTableModelChangeEvent::ColumnsRemoved:
737 case QAccessibleTableModelChangeEvent::RowsRemoved: {
738 int deletedColumns = event->lastColumn() - event->firstColumn() + 1;
739 int deletedRows = event->lastRow() - event->firstRow() + 1;
740 ChildCache newCache;
741 ChildCache::ConstIterator iter = childToId.constBegin();
742 while (iter != childToId.constEnd()) {
743 QAccessible::Id id = iter.value();
744 QAccessibleInterface *iface = QAccessible::accessibleInterface(uniqueId: id);
745 Q_ASSERT(iface);
746 if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) {
747 Q_ASSERT(iface->tableCellInterface());
748 QAccessibleTableCell *cell = static_cast<QAccessibleTableCell*>(iface->tableCellInterface());
749 // Since it is a QPersistentModelIndex, we only need to check if it is valid
750 if (cell->m_index.isValid())
751 newCache.insert(key: indexOfChild(iface: cell), value: id);
752 else
753 QAccessible::deleteAccessibleInterface(uniqueId: id);
754 } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsRemoved
755 && iface->role() == QAccessible::RowHeader) {
756 QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface);
757 if (cell->index < event->firstRow()) {
758 newCache.insert(key: indexOfChild(iface: cell), value: id);
759 } else if (cell->index > event->lastRow()) {
760 cell->index -= deletedRows;
761 newCache.insert(key: indexOfChild(iface: cell), value: id);
762 } else {
763 QAccessible::deleteAccessibleInterface(uniqueId: id);
764 }
765 } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsRemoved
766 && iface->role() == QAccessible::ColumnHeader) {
767 QAccessibleTableHeaderCell *cell = static_cast<QAccessibleTableHeaderCell*>(iface);
768 if (cell->index < event->firstColumn()) {
769 newCache.insert(key: indexOfChild(iface: cell), value: id);
770 } else if (cell->index > event->lastColumn()) {
771 cell->index -= deletedColumns;
772 newCache.insert(key: indexOfChild(iface: cell), value: id);
773 } else {
774 QAccessible::deleteAccessibleInterface(uniqueId: id);
775 }
776 }
777 ++iter;
778 }
779 childToId = newCache;
780 break;
781 }
782
783 case QAccessibleTableModelChangeEvent::DataChanged:
784 // nothing to do in this case
785 break;
786 }
787}
788
789#if QT_CONFIG(treeview)
790
791// TREE VIEW
792
793QModelIndex QAccessibleTree::indexFromLogical(int row, int column) const
794{
795 if (!isValid() || !view()->model())
796 return QModelIndex();
797
798 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view());
799 if (Q_UNLIKELY(row < 0 || column < 0 || treeView->d_func()->viewItems.size() <= row)) {
800 qWarning() << "QAccessibleTree::indexFromLogical: invalid index: " << row << column << " for " << treeView;
801 return QModelIndex();
802 }
803 QModelIndex modelIndex = treeView->d_func()->viewItems.at(i: row).index;
804
805 if (modelIndex.isValid() && column > 0) {
806 modelIndex = view()->model()->index(row: modelIndex.row(), column, parent: modelIndex.parent());
807 }
808 return modelIndex;
809}
810
811QAccessibleInterface *QAccessibleTree::childAt(int x, int y) const
812{
813 const QAbstractItemView *theView = view();
814 if (!theView)
815 return nullptr;
816 const QAbstractItemModel *theModel = theView->model();
817 if (!theModel)
818 return nullptr;
819
820 const QPoint viewportOffset = theView->viewport()->mapTo(theView, QPoint(0, 0));
821 const QPoint indexPosition = theView->mapFromGlobal(QPoint(x, y) - viewportOffset);
822
823 const QModelIndex index = theView->indexAt(point: indexPosition);
824 if (!index.isValid())
825 return nullptr;
826
827 const QTreeView *treeView = qobject_cast<const QTreeView *>(object: theView);
828 int row = treeView->d_func()->viewIndex(index) + (horizontalHeader() ? 1 : 0);
829 int column = index.column();
830
831 int i = row * theModel->columnCount(parent: theView->rootIndex()) + column;
832 return child(index: i);
833}
834
835QAccessibleInterface *QAccessibleTree::focusChild() const
836{
837 const QAbstractItemView *theView = view();
838 if (!theView)
839 return nullptr;
840 const QAbstractItemModel *theModel = theView->model();
841 const QModelIndex index = theView->currentIndex();
842 if (!index.isValid())
843 return nullptr;
844
845 const QTreeView *treeView = qobject_cast<const QTreeView *>(object: theView);
846 const int row = treeView->d_func()->viewIndex(index) + (horizontalHeader() ? 1 : 0);
847 const int column = index.column();
848
849 int i = row * theModel->columnCount(parent: theView->rootIndex()) + column;
850 return child(index: i);
851}
852
853int QAccessibleTree::childCount() const
854{
855 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view());
856 if (!treeView)
857 return 0;
858 const QAbstractItemModel *theModel = treeView->model();
859 if (!theModel)
860 return 0;
861
862 int hHeader = horizontalHeader() ? 1 : 0;
863 return (treeView->d_func()->viewItems.size() + hHeader)
864 * theModel->columnCount(parent: treeView->rootIndex());
865}
866
867QAccessibleInterface *QAccessibleTree::child(int logicalIndex) const
868{
869 QAbstractItemView *theView = view();
870 if (!theView)
871 return nullptr;
872 const QAbstractItemModel *theModel = theView->model();
873 const QModelIndex rootIndex = theView->rootIndex();
874 if (logicalIndex < 0 || !theModel || !theModel->columnCount(parent: rootIndex))
875 return nullptr;
876
877 auto id = childToId.constFind(key: logicalIndex);
878 if (id != childToId.constEnd())
879 return QAccessible::accessibleInterface(uniqueId: id.value());
880
881 QAccessibleInterface *iface = nullptr;
882 int index = logicalIndex;
883
884 if (horizontalHeader()) {
885 if (index < theModel->columnCount(parent: rootIndex))
886 iface = new QAccessibleTableHeaderCell(theView, index, Qt::Horizontal);
887 else
888 index -= theModel->columnCount(parent: rootIndex);
889 }
890
891 if (!iface) {
892 const int row = index / theModel->columnCount(parent: rootIndex);
893 const int column = index % theModel->columnCount(parent: rootIndex);
894 const QModelIndex modelIndex = indexFromLogical(row, column);
895 if (!modelIndex.isValid())
896 return nullptr;
897 iface = new QAccessibleTableCell(theView, modelIndex, cellRole());
898 }
899 QAccessible::registerAccessibleInterface(iface);
900 childToId.insert(key: logicalIndex, value: QAccessible::uniqueId(iface));
901 return iface;
902}
903
904int QAccessibleTree::rowCount() const
905{
906 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view());
907 if (!treeView)
908 return 0;
909 return treeView->d_func()->viewItems.size();
910}
911
912int QAccessibleTree::indexOfChild(const QAccessibleInterface *iface) const
913{
914 const QAbstractItemView *theView = view();
915 if (!theView)
916 return -1;
917 const QAbstractItemModel *theModel = theView->model();
918 if (!theModel)
919 return -1;
920 QAccessibleInterface *parent = iface->parent();
921 if (parent->object() != theView)
922 return -1;
923
924 if (iface->role() == QAccessible::TreeItem) {
925 const QAccessibleTableCell* cell = static_cast<const QAccessibleTableCell*>(iface);
926 const QTreeView *treeView = qobject_cast<const QTreeView *>(object: theView);
927 Q_ASSERT(treeView);
928 int row = treeView->d_func()->viewIndex(index: cell->m_index) + (horizontalHeader() ? 1 : 0);
929 int column = cell->m_index.column();
930
931 int index = row * theModel->columnCount(parent: theView->rootIndex()) + column;
932 return index;
933 } else if (iface->role() == QAccessible::ColumnHeader){
934 const QAccessibleTableHeaderCell* cell = static_cast<const QAccessibleTableHeaderCell*>(iface);
935 return cell->index;
936 } else {
937 qWarning() << "WARNING QAccessibleTable::indexOfChild invalid child"
938 << iface->role() << iface->text(t: QAccessible::Name);
939 }
940 // FIXME: add scrollbars and don't just ignore them
941 return -1;
942}
943
944QAccessibleInterface *QAccessibleTree::cellAt(int row, int column) const
945{
946 QModelIndex index = indexFromLogical(row, column);
947 if (Q_UNLIKELY(!index.isValid())) {
948 qWarning(msg: "Requested invalid tree cell: %d %d", row, column);
949 return nullptr;
950 }
951 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view());
952 Q_ASSERT(treeView);
953 int logicalIndex = treeView->d_func()->accessibleTable2Index(index);
954
955 return child(logicalIndex);
956}
957
958QString QAccessibleTree::rowDescription(int) const
959{
960 return QString(); // no headers for rows in trees
961}
962
963bool QAccessibleTree::isRowSelected(int row) const
964{
965 if (!view()->selectionModel())
966 return false;
967 QModelIndex index = indexFromLogical(row);
968 return view()->selectionModel()->isRowSelected(row: index.row(), parent: index.parent());
969}
970
971bool QAccessibleTree::selectRow(int row)
972{
973 if (!view()->selectionModel())
974 return false;
975 QModelIndex index = indexFromLogical(row);
976
977 if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns)
978 return false;
979
980 switch (view()->selectionMode()) {
981 case QAbstractItemView::NoSelection:
982 return false;
983 case QAbstractItemView::SingleSelection:
984 if ((view()->selectionBehavior() != QAbstractItemView::SelectRows) && (columnCount() > 1))
985 return false;
986 view()->clearSelection();
987 break;
988 case QAbstractItemView::ContiguousSelection:
989 if ((!row || !view()->selectionModel()->isRowSelected(row: row - 1, parent: view()->rootIndex()))
990 && !view()->selectionModel()->isRowSelected(row: row + 1, parent: view()->rootIndex()))
991 view()->clearSelection();
992 break;
993 default:
994 break;
995 }
996
997 view()->selectionModel()->select(index, command: QItemSelectionModel::Select | QItemSelectionModel::Rows);
998 return true;
999}
1000
1001#endif // QT_CONFIG(treeview)
1002
1003#if QT_CONFIG(listview)
1004
1005// LIST VIEW
1006
1007QAccessibleInterface *QAccessibleList::child(int logicalIndex) const
1008{
1009 QAbstractItemView *theView = view();
1010 if (!theView)
1011 return nullptr;
1012 const QAbstractItemModel *theModel = theView->model();
1013 if (!theModel)
1014 return nullptr;
1015
1016 if (columnCount() == 0) {
1017 return nullptr;
1018 }
1019
1020 const auto id = childToId.constFind(key: logicalIndex);
1021 if (id != childToId.constEnd())
1022 return QAccessible::accessibleInterface(uniqueId: id.value());
1023
1024 const QListView *listView = qobject_cast<const QListView*>(object: theView);
1025 Q_ASSERT(listView);
1026 int row = logicalIndex;
1027 int column = listView->modelColumn();
1028
1029 const QModelIndex rootIndex = theView->rootIndex();
1030 const QModelIndex index = theModel->index(row, column, parent: rootIndex);
1031 if (Q_UNLIKELY(!index.isValid())) {
1032 qWarning(msg: "QAccessibleList::child: Invalid index at: %d %d", row, column);
1033 return nullptr;
1034 }
1035 const auto iface = new QAccessibleTableCell(theView, index, cellRole());
1036
1037 QAccessible::registerAccessibleInterface(iface);
1038 childToId.insert(key: logicalIndex, value: QAccessible::uniqueId(iface));
1039 return iface;
1040}
1041
1042QAccessibleInterface *QAccessibleList::cellAt(int row, int column) const
1043{
1044 if (column != 0)
1045 return nullptr;
1046
1047 return child(logicalIndex: row);
1048}
1049
1050int QAccessibleList::selectedCellCount() const
1051{
1052 QAbstractItemView *theView = view();
1053 if (!theView->selectionModel())
1054 return 0;
1055 const QListView *listView = qobject_cast<const QListView*>(object: theView);
1056 const int modelColumn = listView->modelColumn();
1057 const QModelIndexList selectedIndexes = theView->selectionModel()->selectedIndexes();
1058 return std::count_if(first: selectedIndexes.cbegin(), last: selectedIndexes.cend(),
1059 pred: [modelColumn](const auto &index) {
1060 return index.column() == modelColumn;
1061 });
1062}
1063
1064QList<QAccessibleInterface *> QAccessibleList::selectedCells() const
1065{
1066 QAbstractItemView *theView = view();
1067 QList<QAccessibleInterface*> cells;
1068 if (!view()->selectionModel() || columnCount() == 0)
1069 return cells;
1070 const QListView *listView = qobject_cast<const QListView*>(object: theView);
1071 const int modelColumn = listView->modelColumn();
1072 const QModelIndexList selectedIndexes = theView->selectionModel()->selectedIndexes();
1073 cells.reserve(asize: qMin(a: selectedIndexes.size(), b: rowCount()));
1074 for (const QModelIndex &index : selectedIndexes)
1075 if (index.column() == modelColumn) {
1076 cells.append(t: child(logicalIndex: index.row()));
1077 }
1078 return cells;
1079}
1080
1081#endif // QT_CONFIG(listview)
1082
1083// TABLE CELL
1084
1085QAccessibleTableCell::QAccessibleTableCell(QAbstractItemView *view_, const QModelIndex &index_, QAccessible::Role role_)
1086 : /* QAccessibleSimpleEditableTextInterface(this), */ view(view_), m_index(index_), m_role(role_)
1087{
1088 if (Q_UNLIKELY(!index_.isValid()))
1089 qWarning() << "QAccessibleTableCell::QAccessibleTableCell with invalid index: " << index_;
1090}
1091
1092void *QAccessibleTableCell::interface_cast(QAccessible::InterfaceType t)
1093{
1094 if (t == QAccessible::TableCellInterface)
1095 return static_cast<QAccessibleTableCellInterface*>(this);
1096 if (t == QAccessible::ActionInterface)
1097 return static_cast<QAccessibleActionInterface*>(this);
1098 return nullptr;
1099}
1100
1101int QAccessibleTableCell::columnExtent() const { return 1; }
1102int QAccessibleTableCell::rowExtent() const { return 1; }
1103
1104QList<QAccessibleInterface*> QAccessibleTableCell::rowHeaderCells() const
1105{
1106 QList<QAccessibleInterface*> headerCell;
1107 if (verticalHeader()) {
1108 // FIXME
1109 headerCell.append(t: new QAccessibleTableHeaderCell(view, m_index.row(), Qt::Vertical));
1110 }
1111 return headerCell;
1112}
1113
1114QList<QAccessibleInterface*> QAccessibleTableCell::columnHeaderCells() const
1115{
1116 QList<QAccessibleInterface*> headerCell;
1117 if (horizontalHeader()) {
1118 // FIXME
1119 headerCell.append(t: new QAccessibleTableHeaderCell(view, m_index.column(), Qt::Horizontal));
1120 }
1121 return headerCell;
1122}
1123
1124QHeaderView *QAccessibleTableCell::horizontalHeader() const
1125{
1126 QHeaderView *header = nullptr;
1127
1128 if (false) {
1129#if QT_CONFIG(tableview)
1130 } else if (const QTableView *tv = qobject_cast<const QTableView*>(object: view)) {
1131 header = tv->horizontalHeader();
1132#endif
1133#if QT_CONFIG(treeview)
1134 } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(object: view)) {
1135 header = tv->header();
1136#endif
1137 }
1138
1139 return header;
1140}
1141
1142QHeaderView *QAccessibleTableCell::verticalHeader() const
1143{
1144 QHeaderView *header = nullptr;
1145#if QT_CONFIG(tableview)
1146 if (const QTableView *tv = qobject_cast<const QTableView*>(object: view))
1147 header = tv->verticalHeader();
1148#endif
1149 return header;
1150}
1151
1152int QAccessibleTableCell::columnIndex() const
1153{
1154 if (!isValid())
1155 return -1;
1156#if QT_CONFIG(listview)
1157 if (role() == QAccessible::ListItem) {
1158 return 0;
1159 }
1160#endif
1161 return m_index.column();
1162}
1163
1164int QAccessibleTableCell::rowIndex() const
1165{
1166 if (!isValid())
1167 return -1;
1168#if QT_CONFIG(treeview)
1169 if (role() == QAccessible::TreeItem) {
1170 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view);
1171 Q_ASSERT(treeView);
1172 int row = treeView->d_func()->viewIndex(index: m_index);
1173 return row;
1174 }
1175#endif
1176 return m_index.row();
1177}
1178
1179bool QAccessibleTableCell::isSelected() const
1180{
1181 if (!isValid())
1182 return false;
1183 return view->selectionModel()->isSelected(index: m_index);
1184}
1185
1186QStringList QAccessibleTableCell::actionNames() const
1187{
1188 QStringList names;
1189 names << toggleAction();
1190 return names;
1191}
1192
1193void QAccessibleTableCell::doAction(const QString& actionName)
1194{
1195 if (actionName == toggleAction()) {
1196#if defined(Q_OS_ANDROID)
1197 QAccessibleInterface *parentInterface = parent();
1198 while (parentInterface){
1199 if (parentInterface->role() == QAccessible::ComboBox) {
1200 selectCell();
1201 parentInterface->actionInterface()->doAction(pressAction());
1202 return;
1203 } else {
1204 parentInterface = parentInterface->parent();
1205 }
1206 }
1207#endif
1208 if (isSelected()) {
1209 unselectCell();
1210 } else {
1211 selectCell();
1212 }
1213 }
1214}
1215
1216QStringList QAccessibleTableCell::keyBindingsForAction(const QString &) const
1217{
1218 return QStringList();
1219}
1220
1221
1222void QAccessibleTableCell::selectCell()
1223{
1224 if (!isValid())
1225 return;
1226 QAbstractItemView::SelectionMode selectionMode = view->selectionMode();
1227 if (selectionMode == QAbstractItemView::NoSelection)
1228 return;
1229 Q_ASSERT(table());
1230 QAccessibleTableInterface *cellTable = table()->tableInterface();
1231
1232 switch (view->selectionBehavior()) {
1233 case QAbstractItemView::SelectItems:
1234 break;
1235 case QAbstractItemView::SelectColumns:
1236 if (cellTable)
1237 cellTable->selectColumn(column: m_index.column());
1238 return;
1239 case QAbstractItemView::SelectRows:
1240 if (cellTable)
1241 cellTable->selectRow(row: m_index.row());
1242 return;
1243 }
1244
1245 if (selectionMode == QAbstractItemView::SingleSelection) {
1246 view->clearSelection();
1247 }
1248
1249 view->selectionModel()->select(index: m_index, command: QItemSelectionModel::Select);
1250}
1251
1252void QAccessibleTableCell::unselectCell()
1253{
1254 if (!isValid())
1255 return;
1256 QAbstractItemView::SelectionMode selectionMode = view->selectionMode();
1257 if (selectionMode == QAbstractItemView::NoSelection)
1258 return;
1259
1260 QAccessibleTableInterface *cellTable = table()->tableInterface();
1261
1262 switch (view->selectionBehavior()) {
1263 case QAbstractItemView::SelectItems:
1264 break;
1265 case QAbstractItemView::SelectColumns:
1266 if (cellTable)
1267 cellTable->unselectColumn(column: m_index.column());
1268 return;
1269 case QAbstractItemView::SelectRows:
1270 if (cellTable)
1271 cellTable->unselectRow(row: m_index.row());
1272 return;
1273 }
1274
1275 //If the mode is not MultiSelection or ExtendedSelection and only
1276 //one cell is selected it cannot be unselected by the user
1277 if ((selectionMode != QAbstractItemView::MultiSelection)
1278 && (selectionMode != QAbstractItemView::ExtendedSelection)
1279 && (view->selectionModel()->selectedIndexes().size() <= 1))
1280 return;
1281
1282 view->selectionModel()->select(index: m_index, command: QItemSelectionModel::Deselect);
1283}
1284
1285QAccessibleInterface *QAccessibleTableCell::table() const
1286{
1287 return QAccessible::queryAccessibleInterface(view);
1288}
1289
1290QAccessible::Role QAccessibleTableCell::role() const
1291{
1292 return m_role;
1293}
1294
1295QAccessible::State QAccessibleTableCell::state() const
1296{
1297 QAccessible::State st;
1298 if (!isValid())
1299 return st;
1300
1301 QRect globalRect = view->rect();
1302 globalRect.translate(p: view->mapToGlobal(QPoint(0,0)));
1303 if (!globalRect.intersects(r: rect()))
1304 st.invisible = true;
1305
1306 if (view->selectionModel()->isSelected(index: m_index))
1307 st.selected = true;
1308 if (view->selectionModel()->currentIndex() == m_index)
1309 st.focused = true;
1310
1311 const QVariant checkState = m_index.data(role: Qt::CheckStateRole);
1312 if (checkState.toInt() == Qt::Checked)
1313 st.checked = true;
1314
1315 Qt::ItemFlags flags = m_index.flags();
1316 if ((flags & Qt::ItemIsUserCheckable) && checkState.isValid())
1317 st.checkable = true;
1318 if (flags & Qt::ItemIsSelectable) {
1319 st.selectable = true;
1320 st.focusable = true;
1321 if (view->selectionMode() == QAbstractItemView::MultiSelection)
1322 st.multiSelectable = true;
1323 if (view->selectionMode() == QAbstractItemView::ExtendedSelection)
1324 st.extSelectable = true;
1325 }
1326#if QT_CONFIG(treeview)
1327 if (m_role == QAccessible::TreeItem) {
1328 const QTreeView *treeView = qobject_cast<const QTreeView*>(object: view);
1329 if (treeView->model()->hasChildren(parent: m_index))
1330 st.expandable = true;
1331 if (treeView->isExpanded(index: m_index))
1332 st.expanded = true;
1333 }
1334#endif
1335 return st;
1336}
1337
1338
1339QRect QAccessibleTableCell::rect() const
1340{
1341 QRect r;
1342 if (!isValid())
1343 return r;
1344 r = view->visualRect(index: m_index);
1345
1346 if (!r.isNull()) {
1347 r.translate(p: view->viewport()->mapTo(view, QPoint(0,0)));
1348 r.translate(p: view->mapToGlobal(QPoint(0, 0)));
1349 }
1350 return r;
1351}
1352
1353QString QAccessibleTableCell::text(QAccessible::Text t) const
1354{
1355 QString value;
1356 if (!isValid())
1357 return value;
1358 QAbstractItemModel *model = view->model();
1359 switch (t) {
1360 case QAccessible::Name:
1361 value = model->data(index: m_index, role: Qt::AccessibleTextRole).toString();
1362 if (value.isEmpty())
1363 value = model->data(index: m_index, role: Qt::DisplayRole).toString();
1364 break;
1365 case QAccessible::Description:
1366 value = model->data(index: m_index, role: Qt::AccessibleDescriptionRole).toString();
1367 break;
1368 default:
1369 break;
1370 }
1371 return value;
1372}
1373
1374void QAccessibleTableCell::setText(QAccessible::Text /*t*/, const QString &text)
1375{
1376 if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable))
1377 return;
1378 view->model()->setData(index: m_index, value: text);
1379}
1380
1381bool QAccessibleTableCell::isValid() const
1382{
1383 return view && !qt_widget_private(widget: view)->data.in_destructor
1384 && view->model() && m_index.isValid();
1385}
1386
1387QAccessibleInterface *QAccessibleTableCell::parent() const
1388{
1389 return QAccessible::queryAccessibleInterface(view);
1390}
1391
1392QAccessibleInterface *QAccessibleTableCell::child(int) const
1393{
1394 return nullptr;
1395}
1396
1397QAccessibleTableHeaderCell::QAccessibleTableHeaderCell(QAbstractItemView *view_, int index_, Qt::Orientation orientation_)
1398 : view(view_), index(index_), orientation(orientation_)
1399{
1400 Q_ASSERT(index_ >= 0);
1401}
1402
1403QAccessible::Role QAccessibleTableHeaderCell::role() const
1404{
1405 if (orientation == Qt::Horizontal)
1406 return QAccessible::ColumnHeader;
1407 return QAccessible::RowHeader;
1408}
1409
1410QAccessible::State QAccessibleTableHeaderCell::state() const
1411{
1412 QAccessible::State s;
1413 if (QHeaderView *h = headerView()) {
1414 s.invisible = !h->testAttribute(attribute: Qt::WA_WState_Visible);
1415 s.disabled = !h->isEnabled();
1416 }
1417 return s;
1418}
1419
1420QRect QAccessibleTableHeaderCell::rect() const
1421{
1422 QHeaderView *header = nullptr;
1423 if (false) {
1424#if QT_CONFIG(tableview)
1425 } else if (const QTableView *tv = qobject_cast<const QTableView*>(object: view)) {
1426 if (orientation == Qt::Horizontal) {
1427 header = tv->horizontalHeader();
1428 } else {
1429 header = tv->verticalHeader();
1430 }
1431#endif
1432#if QT_CONFIG(treeview)
1433 } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(object: view)) {
1434 header = tv->header();
1435#endif
1436 }
1437 if (!header)
1438 return QRect();
1439 QPoint zero = header->mapToGlobal(QPoint(0, 0));
1440 int sectionSize = header->sectionSize(logicalIndex: index);
1441 int sectionPos = header->sectionPosition(logicalIndex: index);
1442 return orientation == Qt::Horizontal
1443 ? QRect(zero.x() + sectionPos, zero.y(), sectionSize, header->height())
1444 : QRect(zero.x(), zero.y() + sectionPos, header->width(), sectionSize);
1445}
1446
1447QString QAccessibleTableHeaderCell::text(QAccessible::Text t) const
1448{
1449 QAbstractItemModel *model = view->model();
1450 QString value;
1451 switch (t) {
1452 case QAccessible::Name:
1453 value = model->headerData(section: index, orientation, role: Qt::AccessibleTextRole).toString();
1454 if (value.isEmpty())
1455 value = model->headerData(section: index, orientation, role: Qt::DisplayRole).toString();
1456 break;
1457 case QAccessible::Description:
1458 value = model->headerData(section: index, orientation, role: Qt::AccessibleDescriptionRole).toString();
1459 break;
1460 default:
1461 break;
1462 }
1463 return value;
1464}
1465
1466void QAccessibleTableHeaderCell::setText(QAccessible::Text, const QString &)
1467{
1468 return;
1469}
1470
1471bool QAccessibleTableHeaderCell::isValid() const
1472{
1473 const QAbstractItemModel *theModel = view && !qt_widget_private(widget: view)->data.in_destructor
1474 ? view->model() : nullptr;
1475 if (!theModel)
1476 return false;
1477 const QModelIndex rootIndex = view->rootIndex();
1478 return (index >= 0) && ((orientation == Qt::Horizontal)
1479 ? (index < theModel->columnCount(parent: rootIndex))
1480 : (index < theModel->rowCount(parent: rootIndex)));
1481}
1482
1483QAccessibleInterface *QAccessibleTableHeaderCell::parent() const
1484{
1485 return QAccessible::queryAccessibleInterface(view);
1486}
1487
1488QAccessibleInterface *QAccessibleTableHeaderCell::child(int) const
1489{
1490 return nullptr;
1491}
1492
1493QHeaderView *QAccessibleTableHeaderCell::headerView() const
1494{
1495 QHeaderView *header = nullptr;
1496 if (false) {
1497#if QT_CONFIG(tableview)
1498 } else if (const QTableView *tv = qobject_cast<const QTableView*>(object: view)) {
1499 if (orientation == Qt::Horizontal) {
1500 header = tv->horizontalHeader();
1501 } else {
1502 header = tv->verticalHeader();
1503 }
1504#endif
1505#if QT_CONFIG(treeview)
1506 } else if (const QTreeView *tv = qobject_cast<const QTreeView*>(object: view)) {
1507 header = tv->header();
1508#endif
1509 }
1510 return header;
1511}
1512
1513QT_END_NAMESPACE
1514
1515#endif // QT_CONFIG(accessibility)
1516

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