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