/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "itemviews_p.h" #include #if QT_CONFIG(tableview) #include #endif #if QT_CONFIG(listview) #include #endif #if QT_CONFIG(treeview) #include #include #endif #include #ifndef QT_NO_ACCESSIBILITY QT_BEGIN_NAMESPACE /* Implementation of the IAccessible2 table2 interface. Much simpler than the other table interfaces since there is only the main table and cells: TABLE/LIST/TREE |- HEADER CELL |- CELL |- CELL ... */ QAbstractItemView *QAccessibleTable::view() const { return qobject_cast(object()); } int QAccessibleTable::logicalIndex(const QModelIndex &index) const { if (!view()->model() || !index.isValid()) return -1; int vHeader = verticalHeader() ? 1 : 0; int hHeader = horizontalHeader() ? 1 : 0; return (index.row() + hHeader)*(index.model()->columnCount() + vHeader) + (index.column() + vHeader); } QAccessibleTable::QAccessibleTable(QWidget *w) : QAccessibleObject(w) { Q_ASSERT(view()); #if QT_CONFIG(tableview) if (qobject_cast(view())) { m_role = QAccessible::Table; } else #endif #if QT_CONFIG(treeview) if (qobject_cast(view())) { m_role = QAccessible::Tree; } else #endif #if QT_CONFIG(listview) if (qobject_cast(view())) { m_role = QAccessible::List; } else #endif { // is this our best guess? m_role = QAccessible::Table; } } bool QAccessibleTable::isValid() const { return (view() && !qobject_cast(view())->d_func()->data.in_destructor); } QAccessibleTable::~QAccessibleTable() { Q_FOREACH (QAccessible::Id id, childToId) QAccessible::deleteAccessibleInterface(id); } QHeaderView *QAccessibleTable::horizontalHeader() const { QHeaderView *header = 0; if (false) { #if QT_CONFIG(tableview) } else if (const QTableView *tv = qobject_cast(view())) { header = tv->horizontalHeader(); #endif #if QT_CONFIG(treeview) } else if (const QTreeView *tv = qobject_cast(view())) { header = tv->header(); #endif } return header; } QHeaderView *QAccessibleTable::verticalHeader() const { QHeaderView *header = 0; if (false) { #if QT_CONFIG(tableview) } else if (const QTableView *tv = qobject_cast(view())) { header = tv->verticalHeader(); #endif } return header; } QAccessibleInterface *QAccessibleTable::cellAt(int row, int column) const { if (!view()->model()) return 0; Q_ASSERT(role() != QAccessible::Tree); QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); if (Q_UNLIKELY(!index.isValid())) { qWarning() << "QAccessibleTable::cellAt: invalid index: " << index << " for " << view(); return 0; } return child(logicalIndex(index)); } QAccessibleInterface *QAccessibleTable::caption() const { return 0; } QString QAccessibleTable::columnDescription(int column) const { if (!view()->model()) return QString(); return view()->model()->headerData(column, Qt::Horizontal).toString(); } int QAccessibleTable::columnCount() const { if (!view()->model()) return 0; return view()->model()->columnCount(); } int QAccessibleTable::rowCount() const { if (!view()->model()) return 0; return view()->model()->rowCount(); } int QAccessibleTable::selectedCellCount() const { if (!view()->selectionModel()) return 0; return view()->selectionModel()->selectedIndexes().count(); } int QAccessibleTable::selectedColumnCount() const { if (!view()->selectionModel()) return 0; return view()->selectionModel()->selectedColumns().count(); } int QAccessibleTable::selectedRowCount() const { if (!view()->selectionModel()) return 0; return view()->selectionModel()->selectedRows().count(); } QString QAccessibleTable::rowDescription(int row) const { if (!view()->model()) return QString(); return view()->model()->headerData(row, Qt::Vertical).toString(); } QList QAccessibleTable::selectedCells() const { QList cells; if (!view()->selectionModel()) return cells; const QModelIndexList selectedIndexes = view()->selectionModel()->selectedIndexes(); cells.reserve(selectedIndexes.size()); Q_FOREACH (const QModelIndex &index, selectedIndexes) cells.append(child(logicalIndex(index))); return cells; } QList QAccessibleTable::selectedColumns() const { if (!view()->selectionModel()) return QList(); QList columns; const QModelIndexList selectedColumns = view()->selectionModel()->selectedColumns(); columns.reserve(selectedColumns.size()); Q_FOREACH (const QModelIndex &index, selectedColumns) columns.append(index.column()); return columns; } QList QAccessibleTable::selectedRows() const { if (!view()->selectionModel()) return QList(); QList rows; const QModelIndexList selectedRows = view()->selectionModel()->selectedRows(); rows.reserve(selectedRows.size()); Q_FOREACH (const QModelIndex &index, selectedRows) rows.append(index.row()); return rows; } QAccessibleInterface *QAccessibleTable::summary() const { return 0; } bool QAccessibleTable::isColumnSelected(int column) const { if (!view()->selectionModel()) return false; return view()->selectionModel()->isColumnSelected(column, QModelIndex()); } bool QAccessibleTable::isRowSelected(int row) const { if (!view()->selectionModel()) return false; return view()->selectionModel()->isRowSelected(row, QModelIndex()); } bool QAccessibleTable::selectRow(int row) { if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) return false; switch (view()->selectionMode()) { case QAbstractItemView::NoSelection: return false; case QAbstractItemView::SingleSelection: if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 ) return false; view()->clearSelection(); break; case QAbstractItemView::ContiguousSelection: if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) view()->clearSelection(); break; default: break; } view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); return true; } bool QAccessibleTable::selectColumn(int column) { if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectRows) return false; switch (view()->selectionMode()) { case QAbstractItemView::NoSelection: return false; case QAbstractItemView::SingleSelection: if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) return false; Q_FALLTHROUGH(); case QAbstractItemView::ContiguousSelection: if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) view()->clearSelection(); break; default: break; } view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Columns); return true; } bool QAccessibleTable::unselectRow(int row) { if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); if (!index.isValid()) return false; QItemSelection selection(index, index); switch (view()->selectionMode()) { case QAbstractItemView::SingleSelection: //In SingleSelection and ContiguousSelection once an item //is selected, there's no way for the user to unselect all items if (selectedRowCount() == 1) return false; break; case QAbstractItemView::ContiguousSelection: if (selectedRowCount() == 1) return false; if ((!row || view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { //If there are rows selected both up the current row and down the current rown, //the ones which are down the current row will be deselected selection = QItemSelection(index, view()->model()->index(rowCount() - 1, 0, view()->rootIndex())); } default: break; } view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); return true; } bool QAccessibleTable::unselectColumn(int column) { if (!view()->model() || !view()->selectionModel()) return false; QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); if (!index.isValid()) return false; QItemSelection selection(index, index); switch (view()->selectionMode()) { case QAbstractItemView::SingleSelection: //In SingleSelection and ContiguousSelection once an item //is selected, there's no way for the user to unselect all items if (selectedColumnCount() == 1) return false; break; case QAbstractItemView::ContiguousSelection: if (selectedColumnCount() == 1) return false; if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { //If there are columns selected both at the left of the current row and at the right //of the current rown, the ones which are at the right will be deselected selection = QItemSelection(index, view()->model()->index(0, columnCount() - 1, view()->rootIndex())); } default: break; } view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Columns); return true; } QAccessible::Role QAccessibleTable::role() const { return m_role; } QAccessible::State QAccessibleTable::state() const { return QAccessible::State(); } QAccessibleInterface *QAccessibleTable::childAt(int x, int y) const { QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); // FIXME: if indexPosition < 0 in one coordinate, return header QModelIndex index = view()->indexAt(indexPosition); if (index.isValid()) { return child(logicalIndex(index)); } return 0; } int QAccessibleTable::childCount() const { if (!view()->model()) return 0; int vHeader = verticalHeader() ? 1 : 0; int hHeader = horizontalHeader() ? 1 : 0; return (view()->model()->rowCount()+hHeader) * (view()->model()->columnCount()+vHeader); } int QAccessibleTable::indexOfChild(const QAccessibleInterface *iface) const { if (!view()->model()) return -1; QAccessibleInterface *parent = iface->parent(); if (parent->object() != view()) return -1; Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { const QAccessibleTableCell* cell = static_cast(iface); return logicalIndex(cell->m_index); } else if (iface->role() == QAccessible::ColumnHeader){ const QAccessibleTableHeaderCell* cell = static_cast(iface); return cell->index + (verticalHeader() ? 1 : 0); } else if (iface->role() == QAccessible::RowHeader){ const QAccessibleTableHeaderCell* cell = static_cast(iface); return (cell->index + 1) * (view()->model()->columnCount() + 1); } else if (iface->role() == QAccessible::Pane) { return 0; // corner button } else { qWarning() << "WARNING QAccessibleTable::indexOfChild Fix my children..." << iface->role() << iface->text(QAccessible::Name); } // FIXME: we are in denial of our children. this should stop. return -1; } QString QAccessibleTable::text(QAccessible::Text t) const { if (t == QAccessible::Description) return view()->accessibleDescription(); return view()->accessibleName(); } QRect QAccessibleTable::rect() const { if (!view()->isVisible()) return QRect(); QPoint pos = view()->mapToGlobal(QPoint(0, 0)); return QRect(pos.x(), pos.y(), view()->width(), view()->height()); } QAccessibleInterface *QAccessibleTable::parent() const { if (view() && view()->parent()) { if (qstrcmp("QComboBoxPrivateContainer", view()->parent()->metaObject()->className()) == 0) { return QAccessible::queryAccessibleInterface(view()->parent()->parent()); } return QAccessible::queryAccessibleInterface(view()->parent()); } return 0; } QAccessibleInterface *QAccessibleTable::child(int logicalIndex) const { if (!view()->model()) return 0; auto id = childToId.constFind(logicalIndex); if (id != childToId.constEnd()) return QAccessible::accessibleInterface(id.value()); int vHeader = verticalHeader() ? 1 : 0; int hHeader = horizontalHeader() ? 1 : 0; int columns = view()->model()->columnCount() + vHeader; int row = logicalIndex / columns; int column = logicalIndex % columns; QAccessibleInterface *iface = 0; if (vHeader) { if (column == 0) { if (hHeader && row == 0) { iface = new QAccessibleTableCornerButton(view()); } else { iface = new QAccessibleTableHeaderCell(view(), row - hHeader, Qt::Vertical); } } --column; } if (!iface && hHeader) { if (row == 0) { iface = new QAccessibleTableHeaderCell(view(), column, Qt::Horizontal); } --row; } if (!iface) { QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); if (Q_UNLIKELY(!index.isValid())) { qWarning("QAccessibleTable::child: Invalid index at: %d %d", row, column); return 0; } iface = new QAccessibleTableCell(view(), index, cellRole()); } QAccessible::registerAccessibleInterface(iface); childToId.insert(logicalIndex, QAccessible::uniqueId(iface)); return iface; } void *QAccessibleTable::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TableInterface) return static_cast(this); return 0; } void QAccessibleTable::modelChange(QAccessibleTableModelChangeEvent *event) { // if there is no cache yet, we don't update anything if (childToId.isEmpty()) return; switch (event->modelChangeType()) { case QAccessibleTableModelChangeEvent::ModelReset: Q_FOREACH (QAccessible::Id id, childToId) QAccessible::deleteAccessibleInterface(id); childToId.clear(); break; // rows are inserted: move every row after that case QAccessibleTableModelChangeEvent::RowsInserted: case QAccessibleTableModelChangeEvent::ColumnsInserted: { int newRows = event->lastRow() - event->firstRow() + 1; int newColumns = event->lastColumn() - event->firstColumn() + 1; ChildCache newCache; ChildCache::ConstIterator iter = childToId.constBegin(); while (iter != childToId.constEnd()) { QAccessible::Id id = iter.value(); QAccessibleInterface *iface = QAccessible::accessibleInterface(id); Q_ASSERT(iface); if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsInserted && iface->role() == QAccessible::RowHeader) { QAccessibleTableHeaderCell *cell = static_cast(iface); if (cell->index >= event->firstRow()) { cell->index += newRows; } } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsInserted && iface->role() == QAccessible::ColumnHeader) { QAccessibleTableHeaderCell *cell = static_cast(iface); if (cell->index >= event->firstColumn()) { cell->index += newColumns; } } if (indexOfChild(iface) >= 0) { newCache.insert(indexOfChild(iface), id); } else { // ### This should really not happen, // but it might if the view has a root index set. // This needs to be fixed. QAccessible::deleteAccessibleInterface(id); } ++iter; } childToId = newCache; break; } case QAccessibleTableModelChangeEvent::ColumnsRemoved: case QAccessibleTableModelChangeEvent::RowsRemoved: { int deletedColumns = event->lastColumn() - event->firstColumn() + 1; int deletedRows = event->lastRow() - event->firstRow() + 1; ChildCache newCache; ChildCache::ConstIterator iter = childToId.constBegin(); while (iter != childToId.constEnd()) { QAccessible::Id id = iter.value(); QAccessibleInterface *iface = QAccessible::accessibleInterface(id); Q_ASSERT(iface); if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { Q_ASSERT(iface->tableCellInterface()); QAccessibleTableCell *cell = static_cast(iface->tableCellInterface()); // Since it is a QPersistentModelIndex, we only need to check if it is valid if (cell->m_index.isValid()) newCache.insert(indexOfChild(cell), id); else QAccessible::deleteAccessibleInterface(id); } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::RowsRemoved && iface->role() == QAccessible::RowHeader) { QAccessibleTableHeaderCell *cell = static_cast(iface); if (cell->index < event->firstRow()) { newCache.insert(indexOfChild(cell), id); } else if (cell->index > event->lastRow()) { cell->index -= deletedRows; newCache.insert(indexOfChild(cell), id); } else { QAccessible::deleteAccessibleInterface(id); } } else if (event->modelChangeType() == QAccessibleTableModelChangeEvent::ColumnsRemoved && iface->role() == QAccessible::ColumnHeader) { QAccessibleTableHeaderCell *cell = static_cast(iface); if (cell->index < event->firstColumn()) { newCache.insert(indexOfChild(cell), id); } else if (cell->index > event->lastColumn()) { cell->index -= deletedColumns; newCache.insert(indexOfChild(cell), id); } else { QAccessible::deleteAccessibleInterface(id); } } ++iter; } childToId = newCache; break; } case QAccessibleTableModelChangeEvent::DataChanged: // nothing to do in this case break; } } #if QT_CONFIG(treeview) // TREE VIEW QModelIndex QAccessibleTree::indexFromLogical(int row, int column) const { if (!isValid() || !view()->model()) return QModelIndex(); const QTreeView *treeView = qobject_cast(view()); if (Q_UNLIKELY(row < 0 || column < 0 || treeView->d_func()->viewItems.count() <= row)) { qWarning() << "QAccessibleTree::indexFromLogical: invalid index: " << row << column << " for " << treeView; return QModelIndex(); } QModelIndex modelIndex = treeView->d_func()->viewItems.at(row).index; if (modelIndex.isValid() && column > 0) { modelIndex = view()->model()->index(modelIndex.row(), column, modelIndex.parent()); } return modelIndex; } QAccessibleInterface *QAccessibleTree::childAt(int x, int y) const { if (!view()->model()) return 0; QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); QModelIndex index = view()->indexAt(indexPosition); if (!index.isValid()) return 0; const QTreeView *treeView = qobject_cast(view()); int row = treeView->d_func()->viewIndex(index) + (horizontalHeader() ? 1 : 0); int column = index.column(); int i = row * view()->model()->columnCount() + column; return child(i); } int QAccessibleTree::childCount() const { const QTreeView *treeView = qobject_cast(view()); Q_ASSERT(treeView); if (!view()->model()) return 0; int hHeader = horizontalHeader() ? 1 : 0; return (treeView->d_func()->viewItems.count() + hHeader)* view()->model()->columnCount(); } QAccessibleInterface *QAccessibleTree::child(int logicalIndex) const { if (logicalIndex < 0 || !view()->model() || !view()->model()->columnCount()) return 0; QAccessibleInterface *iface = 0; int index = logicalIndex; if (horizontalHeader()) { if (index < view()->model()->columnCount()) { iface = new QAccessibleTableHeaderCell(view(), index, Qt::Horizontal); } else { index -= view()->model()->columnCount(); } } if (!iface) { int row = index / view()->model()->columnCount(); int column = index % view()->model()->columnCount(); QModelIndex modelIndex = indexFromLogical(row, column); if (!modelIndex.isValid()) return 0; iface = new QAccessibleTableCell(view(), modelIndex, cellRole()); } QAccessible::registerAccessibleInterface(iface); // ### FIXME: get interfaces from the cache instead of re-creating them return iface; } int QAccessibleTree::rowCount() const { const QTreeView *treeView = qobject_cast(view()); Q_ASSERT(treeView); return treeView->d_func()->viewItems.count(); } int QAccessibleTree::indexOfChild(const QAccessibleInterface *iface) const { if (!view()->model()) return -1; QAccessibleInterface *parent = iface->parent(); if (parent->object() != view()) return -1; if (iface->role() == QAccessible::TreeItem) { const QAccessibleTableCell* cell = static_cast(iface); const QTreeView *treeView = qobject_cast(view()); Q_ASSERT(treeView); int row = treeView->d_func()->viewIndex(cell->m_index) + (horizontalHeader() ? 1 : 0); int column = cell->m_index.column(); int index = row * view()->model()->columnCount() + column; return index; } else if (iface->role() == QAccessible::ColumnHeader){ const QAccessibleTableHeaderCell* cell = static_cast(iface); return cell->index; } else { qWarning() << "WARNING QAccessibleTable::indexOfChild invalid child" << iface->role() << iface->text(QAccessible::Name); } // FIXME: add scrollbars and don't just ignore them return -1; } QAccessibleInterface *QAccessibleTree::cellAt(int row, int column) const { QModelIndex index = indexFromLogical(row, column); if (Q_UNLIKELY(!index.isValid())) { qWarning("Requested invalid tree cell: %d %d", row, column); return 0; } const QTreeView *treeView = qobject_cast(view()); Q_ASSERT(treeView); int logicalIndex = treeView->d_func()->accessibleTable2Index(index); return child(logicalIndex); // FIXME ### new QAccessibleTableCell(view(), index, cellRole()); } QString QAccessibleTree::rowDescription(int) const { return QString(); // no headers for rows in trees } bool QAccessibleTree::isRowSelected(int row) const { if (!view()->selectionModel()) return false; QModelIndex index = indexFromLogical(row); return view()->selectionModel()->isRowSelected(index.row(), index.parent()); } bool QAccessibleTree::selectRow(int row) { if (!view()->selectionModel()) return false; QModelIndex index = indexFromLogical(row); if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) return false; switch (view()->selectionMode()) { case QAbstractItemView::NoSelection: return false; case QAbstractItemView::SingleSelection: if ((view()->selectionBehavior() != QAbstractItemView::SelectRows) && (columnCount() > 1)) return false; view()->clearSelection(); break; case QAbstractItemView::ContiguousSelection: if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) view()->clearSelection(); break; default: break; } view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); return true; } #endif // QT_CONFIG(treeview) // TABLE CELL QAccessibleTableCell::QAccessibleTableCell(QAbstractItemView *view_, const QModelIndex &index_, QAccessible::Role role_) : /* QAccessibleSimpleEditableTextInterface(this), */ view(view_), m_index(index_), m_role(role_) { if (Q_UNLIKELY(!index_.isValid())) qWarning() << "QAccessibleTableCell::QAccessibleTableCell with invalid index: " << index_; } void *QAccessibleTableCell::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::TableCellInterface) return static_cast(this); if (t == QAccessible::ActionInterface) return static_cast(this); return 0; } int QAccessibleTableCell::columnExtent() const { return 1; } int QAccessibleTableCell::rowExtent() const { return 1; } QList QAccessibleTableCell::rowHeaderCells() const { QList headerCell; if (verticalHeader()) { // FIXME headerCell.append(new QAccessibleTableHeaderCell(view, m_index.row(), Qt::Vertical)); } return headerCell; } QList QAccessibleTableCell::columnHeaderCells() const { QList headerCell; if (horizontalHeader()) { // FIXME headerCell.append(new QAccessibleTableHeaderCell(view, m_index.column(), Qt::Horizontal)); } return headerCell; } QHeaderView *QAccessibleTableCell::horizontalHeader() const { QHeaderView *header = 0; if (false) { #if QT_CONFIG(tableview) } else if (const QTableView *tv = qobject_cast(view)) { header = tv->horizontalHeader(); #endif #if QT_CONFIG(treeview) } else if (const QTreeView *tv = qobject_cast(view)) { header = tv->header(); #endif } return header; } QHeaderView *QAccessibleTableCell::verticalHeader() const { QHeaderView *header = 0; #if QT_CONFIG(tableview) if (const QTableView *tv = qobject_cast(view)) header = tv->verticalHeader(); #endif return header; } int QAccessibleTableCell::columnIndex() const { return m_index.column(); } int QAccessibleTableCell::rowIndex() const { #if QT_CONFIG(treeview) if (role() == QAccessible::TreeItem) { const QTreeView *treeView = qobject_cast(view); Q_ASSERT(treeView); int row = treeView->d_func()->viewIndex(m_index); return row; } #endif return m_index.row(); } bool QAccessibleTableCell::isSelected() const { return view->selectionModel()->isSelected(m_index); } QStringList QAccessibleTableCell::actionNames() const { QStringList names; names << toggleAction(); return names; } void QAccessibleTableCell::doAction(const QString& actionName) { if (actionName == toggleAction()) { if (isSelected()) unselectCell(); else selectCell(); } } QStringList QAccessibleTableCell::keyBindingsForAction(const QString &) const { return QStringList(); } void QAccessibleTableCell::selectCell() { QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) return; Q_ASSERT(table()); QAccessibleTableInterface *cellTable = table()->tableInterface(); switch (view->selectionBehavior()) { case QAbstractItemView::SelectItems: break; case QAbstractItemView::SelectColumns: if (cellTable) cellTable->selectColumn(m_index.column()); return; case QAbstractItemView::SelectRows: if (cellTable) cellTable->selectRow(m_index.row()); return; } if (selectionMode == QAbstractItemView::SingleSelection) { view->clearSelection(); } view->selectionModel()->select(m_index, QItemSelectionModel::Select); } void QAccessibleTableCell::unselectCell() { QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); if (!m_index.isValid() || (selectionMode & QAbstractItemView::NoSelection)) return; QAccessibleTableInterface *cellTable = table()->tableInterface(); switch (view->selectionBehavior()) { case QAbstractItemView::SelectItems: break; case QAbstractItemView::SelectColumns: if (cellTable) cellTable->unselectColumn(m_index.column()); return; case QAbstractItemView::SelectRows: if (cellTable) cellTable->unselectRow(m_index.row()); return; } //If the mode is not MultiSelection or ExtendedSelection and only //one cell is selected it cannot be unselected by the user if ((selectionMode != QAbstractItemView::MultiSelection) && (selectionMode != QAbstractItemView::ExtendedSelection) && (view->selectionModel()->selectedIndexes().count() <= 1)) return; view->selectionModel()->select(m_index, QItemSelectionModel::Deselect); } QAccessibleInterface *QAccessibleTableCell::table() const { return QAccessible::queryAccessibleInterface(view); } QAccessible::Role QAccessibleTableCell::role() const { return m_role; } QAccessible::State QAccessibleTableCell::state() const { QAccessible::State st; if (!view) return st; QRect globalRect = view->rect(); globalRect.translate(view->mapToGlobal(QPoint(0,0))); if (!globalRect.intersects(rect())) st.invisible = true; if (view->selectionModel()->isSelected(m_index)) st.selected = true; if (view->selectionModel()->currentIndex() == m_index) st.focused = true; if (m_index.model()->data(m_index, Qt::CheckStateRole).toInt() == Qt::Checked) st.checked = true; Qt::ItemFlags flags = m_index.flags(); if (flags & Qt::ItemIsSelectable) { st.selectable = true; st.focusable = true; if (view->selectionMode() == QAbstractItemView::MultiSelection) st.multiSelectable = true; if (view->selectionMode() == QAbstractItemView::ExtendedSelection) st.extSelectable = true; } #if QT_CONFIG(treeview) if (m_role == QAccessible::TreeItem) { const QTreeView *treeView = qobject_cast(view); if (treeView->model()->hasChildren(m_index)) st.expandable = true; if (treeView->isExpanded(m_index)) st.expanded = true; } #endif return st; } QRect QAccessibleTableCell::rect() const { QRect r; r = view->visualRect(m_index); if (!r.isNull()) { r.translate(view->viewport()->mapTo(view, QPoint(0,0))); r.translate(view->mapToGlobal(QPoint(0, 0))); } return r; } QString QAccessibleTableCell::text(QAccessible::Text t) const { QAbstractItemModel *model = view->model(); QString value; switch (t) { case QAccessible::Name: value = model->data(m_index, Qt::AccessibleTextRole).toString(); if (value.isEmpty()) value = model->data(m_index, Qt::DisplayRole).toString(); break; case QAccessible::Description: value = model->data(m_index, Qt::AccessibleDescriptionRole).toString(); break; default: break; } return value; } void QAccessibleTableCell::setText(QAccessible::Text /*t*/, const QString &text) { if (!(m_index.flags() & Qt::ItemIsEditable)) return; view->model()->setData(m_index, text); } bool QAccessibleTableCell::isValid() const { return view && view->model() && m_index.isValid(); } QAccessibleInterface *QAccessibleTableCell::parent() const { return QAccessible::queryAccessibleInterface(view); } QAccessibleInterface *QAccessibleTableCell::child(int) const { return 0; } QAccessibleTableHeaderCell::QAccessibleTableHeaderCell(QAbstractItemView *view_, int index_, Qt::Orientation orientation_) : view(view_), index(index_), orientation(orientation_) { Q_ASSERT(index_ >= 0); } QAccessible::Role QAccessibleTableHeaderCell::role() const { if (orientation == Qt::Horizontal) return QAccessible::ColumnHeader; return QAccessible::RowHeader; } QAccessible::State QAccessibleTableHeaderCell::state() const { QAccessible::State s; if (QHeaderView *h = headerView()) { s.invisible = !h->testAttribute(Qt::WA_WState_Visible); s.disabled = !h->isEnabled(); } return s; } QRect QAccessibleTableHeaderCell::rect() const { QHeaderView *header = 0; if (false) { #if QT_CONFIG(tableview) } else if (const QTableView *tv = qobject_cast(view)) { if (orientation == Qt::Horizontal) { header = tv->horizontalHeader(); } else { header = tv->verticalHeader(); } #endif #if QT_CONFIG(treeview) } else if (const QTreeView *tv = qobject_cast(view)) { header = tv->header(); #endif } if (!header) return QRect(); QPoint zero = header->mapToGlobal(QPoint(0, 0)); int sectionSize = header->sectionSize(index); int sectionPos = header->sectionPosition(index); return orientation == Qt::Horizontal ? QRect(zero.x() + sectionPos, zero.y(), sectionSize, header->height()) : QRect(zero.x(), zero.y() + sectionPos, header->width(), sectionSize); } QString QAccessibleTableHeaderCell::text(QAccessible::Text t) const { QAbstractItemModel *model = view->model(); QString value; switch (t) { case QAccessible::Name: value = model->headerData(index, orientation, Qt::AccessibleTextRole).toString(); if (value.isEmpty()) value = model->headerData(index, orientation, Qt::DisplayRole).toString(); break; case QAccessible::Description: value = model->headerData(index, orientation, Qt::AccessibleDescriptionRole).toString(); break; default: break; } return value; } void QAccessibleTableHeaderCell::setText(QAccessible::Text, const QString &) { return; } bool QAccessibleTableHeaderCell::isValid() const { return view && view->model() && (index >= 0) && ((orientation == Qt::Horizontal) ? (index < view->model()->columnCount()) : (index < view->model()->rowCount())); } QAccessibleInterface *QAccessibleTableHeaderCell::parent() const { return QAccessible::queryAccessibleInterface(view); } QAccessibleInterface *QAccessibleTableHeaderCell::child(int) const { return 0; } QHeaderView *QAccessibleTableHeaderCell::headerView() const { QHeaderView *header = 0; if (false) { #if QT_CONFIG(tableview) } else if (const QTableView *tv = qobject_cast(view)) { if (orientation == Qt::Horizontal) { header = tv->horizontalHeader(); } else { header = tv->verticalHeader(); } #endif #if QT_CONFIG(treeview) } else if (const QTreeView *tv = qobject_cast(view)) { header = tv->header(); #endif } return header; } QT_END_NAMESPACE #endif // QT_NO_ACCESSIBILITY