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