diff -r 41300fa6a67c -r f7bc934e204c util/src/gui/itemviews/qtableview.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/src/gui/itemviews/qtableview.cpp Wed Mar 31 11:06:36 2010 +0300 @@ -0,0 +1,3193 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtableview.h" + +#ifndef QT_NO_TABLEVIEW +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_ACCESSIBILITY +#include +#endif + +QT_BEGIN_NAMESPACE + +/** \internal + Add a span to the collection. the collection takes the ownership. + */ +void QSpanCollection::addSpan(QSpanCollection::Span *span) +{ + spans.append(span); + Index::iterator it_y = index.lowerBound(-span->top()); + if (it_y == index.end() || it_y.key() != -span->top()) { + //there is no spans that starts with the row in the index, so create a sublist for it. + SubIndex sub_index; + if (it_y != index.end()) { + //the previouslist is the list of spans that sarts _before_ the row of the span. + // and which may intersect this row. + const SubIndex previousList = it_y.value(); + foreach(Span *s, previousList) { + //If a subspans intersect the row, we need to split it into subspans + if(s->bottom() >= span->top()) + sub_index.insert(-s->left(), s); + } + } + it_y = index.insert(-span->top(), sub_index); + //we will insert span to *it_y in the later loop + } + + //insert the span as supspan in all the lists that intesects the span + while(-it_y.key() <= span->bottom()) { + (*it_y).insert(-span->left(), span); + if(it_y == index.begin()) + break; + --it_y; + } +} + + +/** \internal +* Has to be called after the height and width of a span is changed. +* +* old_height is the height before the change +* +* if the size of the span is now 0x0 the span will be deleted. +*/ +void QSpanCollection::updateSpan(QSpanCollection::Span *span, int old_height) +{ + if (old_height < span->height()) { + //add the span as subspan in all the lists that intersect the new covered columns + Index::iterator it_y = index.lowerBound(-(span->top() + old_height - 1)); + Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list + while (-it_y.key() <= span->bottom()) { + (*it_y).insert(-span->left(), span); + if(it_y == index.begin()) + break; + --it_y; + } + } else if (old_height > span->height()) { + //remove the span from all the subspans lists that intersect the columns not covered anymore + Index::iterator it_y = index.lowerBound(-span->bottom()); + if (it_y == index.end()) + it_y = index.find(-span->top()); // This is the only span remaining and we are deleting it. + Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list + while (-it_y.key() <= span->top() + old_height -1) { + if (-it_y.key() > span->bottom()) { + (*it_y).remove(-span->left()); + if (it_y->isEmpty()) { + it_y = index.erase(it_y) - 1; + } + } + if(it_y == index.begin()) + break; + --it_y; + } + } + + if (span->width() == 0 && span->height() == 0) { + spans.removeOne(span); + delete span; + } +} + +/** \internal + * \return a spans that spans over cell x,y (column,row) or 0 if there is none. + */ +QSpanCollection::Span *QSpanCollection::spanAt(int x, int y) const +{ + Index::const_iterator it_y = index.lowerBound(-y); + if (it_y == index.end()) + return 0; + SubIndex::const_iterator it_x = (*it_y).lowerBound(-x); + if (it_x == (*it_y).end()) + return 0; + Span *span = *it_x; + if (span->right() >= x && span->bottom() >= y) + return span; + return 0; +} + + +/** \internal +* remove and deletes all spans inside the collection +*/ +void QSpanCollection::clear() +{ + qDeleteAll(spans); + index.clear(); + spans.clear(); +} + +/** \internal + * return a list to all the spans that spans over cells in the given rectangle + */ +QList QSpanCollection::spansInRect(int x, int y, int w, int h) const +{ + QSet list; + Index::const_iterator it_y = index.lowerBound(-y); + if(it_y == index.end()) + --it_y; + while(-it_y.key() <= y + h) { + SubIndex::const_iterator it_x = (*it_y).lowerBound(-x); + if (it_x == (*it_y).end()) + --it_x; + while(-it_x.key() <= x + w) { + Span *s = *it_x; + if (s->bottom() >= y && s->right() >= x) + list << s; + if (it_x == (*it_y).begin()) + break; + --it_x; + } + if(it_y == index.begin()) + break; + --it_y; + } + return list.toList(); +} + +#undef DEBUG_SPAN_UPDATE + +#ifdef DEBUG_SPAN_UPDATE +QDebug operator<<(QDebug str, const QSpanCollection::Span &span) +{ + str << "(" << span.top() << "," << span.left() << "," << span.bottom() << "," << span.right() << ")"; + return str; +} +#endif + +/** \internal +* Updates the span collection after row insertion. +*/ +void QSpanCollection::updateInsertedRows(int start, int end) +{ +#ifdef DEBUG_SPAN_UPDATE + qDebug() << Q_FUNC_INFO; + qDebug() << start << end; + qDebug() << index; +#endif + if (spans.isEmpty()) + return; + + int delta = end - start + 1; +#ifdef DEBUG_SPAN_UPDATE + qDebug("Before"); +#endif + for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) { + Span *span = *it; +#ifdef DEBUG_SPAN_UPDATE + qDebug() << span << *span; +#endif + if (span->m_bottom < start) + continue; + if (span->m_top >= start) + span->m_top += delta; + span->m_bottom += delta; + } + +#ifdef DEBUG_SPAN_UPDATE + qDebug("After"); + foreach (QSpanCollection::Span *span, spans) + qDebug() << span << *span; +#endif + + for (Index::iterator it_y = index.begin(); it_y != index.end(); ) { + int y = -it_y.key(); + if (y < start) { + ++it_y; + continue; + } + + index.insert(-y - delta, it_y.value()); + it_y = index.erase(it_y); + } +#ifdef DEBUG_SPAN_UPDATE + qDebug() << index; +#endif +} + +/** \internal +* Updates the span collection after column insertion. +*/ +void QSpanCollection::updateInsertedColumns(int start, int end) +{ +#ifdef DEBUG_SPAN_UPDATE + qDebug() << Q_FUNC_INFO; + qDebug() << start << end; + qDebug() << index; +#endif + if (spans.isEmpty()) + return; + + int delta = end - start + 1; +#ifdef DEBUG_SPAN_UPDATE + qDebug("Before"); +#endif + for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) { + Span *span = *it; +#ifdef DEBUG_SPAN_UPDATE + qDebug() << span << *span; +#endif + if (span->m_right < start) + continue; + if (span->m_left >= start) + span->m_left += delta; + span->m_right += delta; + } + +#ifdef DEBUG_SPAN_UPDATE + qDebug("After"); + foreach (QSpanCollection::Span *span, spans) + qDebug() << span << *span; +#endif + + for (Index::iterator it_y = index.begin(); it_y != index.end(); ++it_y) { + SubIndex &subindex = it_y.value(); + for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) { + int x = -it.key(); + if (x < start) { + ++it; + continue; + } + subindex.insert(-x - delta, it.value()); + it = subindex.erase(it); + } + } +#ifdef DEBUG_SPAN_UPDATE + qDebug() << index; +#endif +} + +/** \internal +* Cleans a subindex from to be deleted spans. The update argument is used +* to move the spans inside the subindex, in case their anchor changed. +* \return true if no span in this subindex starts at y, and should thus be deleted. +*/ +bool QSpanCollection::cleanSpanSubIndex(QSpanCollection::SubIndex &subindex, int y, bool update) +{ + if (subindex.isEmpty()) + return true; + + bool should_be_deleted = true; + SubIndex::iterator it = subindex.end(); + do { + --it; + int x = -it.key(); + Span *span = it.value(); + if (span->will_be_deleted) { + it = subindex.erase(it); + continue; + } + if (update && span->m_left != x) { + subindex.insert(-span->m_left, span); + it = subindex.erase(it); + } + if (should_be_deleted && span->m_top == y) + should_be_deleted = false; + } while (it != subindex.begin()); + + return should_be_deleted; +} + +/** \internal +* Updates the span collection after row removal. +*/ +void QSpanCollection::updateRemovedRows(int start, int end) +{ +#ifdef DEBUG_SPAN_UPDATE + qDebug() << Q_FUNC_INFO; + qDebug() << start << end; + qDebug() << index; +#endif + if (spans.isEmpty()) + return; + + SpanList spansToBeDeleted; + int delta = end - start + 1; +#ifdef DEBUG_SPAN_UPDATE + qDebug("Before"); +#endif + for (SpanList::iterator it = spans.begin(); it != spans.end(); ) { + Span *span = *it; +#ifdef DEBUG_SPAN_UPDATE + qDebug() << span << *span; +#endif + if (span->m_bottom < start) { + ++it; + continue; + } + if (span->m_top < start) { + if (span->m_bottom <= end) + span->m_bottom = start - 1; + else + span->m_bottom -= delta; + } else { + if (span->m_bottom > end) { + if (span->m_top <= end) + span->m_top = start; + else + span->m_top -= delta; + span->m_bottom -= delta; + } else { + span->will_be_deleted = true; + } + } + if (span->m_top == span->m_bottom && span->m_left == span->m_right) + span->will_be_deleted = true; + if (span->will_be_deleted) { + spansToBeDeleted.append(span); + it = spans.erase(it); + } else { + ++it; + } + } + +#ifdef DEBUG_SPAN_UPDATE + qDebug("After"); + foreach (QSpanCollection::Span *span, spans) + qDebug() << span << *span; +#endif + if (spans.isEmpty()) { + qDeleteAll(spansToBeDeleted); + index.clear(); + return; + } + + Index::iterator it_y = index.end(); + do { + --it_y; + int y = -it_y.key(); + SubIndex &subindex = it_y.value(); + if (y < start) { + if (cleanSpanSubIndex(subindex, y)) + it_y = index.erase(it_y); + } else if (y >= start && y <= end) { + bool span_at_start = false; + SubIndex spansToBeMoved; + for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ++it) { + Span *span = it.value(); + if (span->will_be_deleted) + continue; + if (!span_at_start && span->m_top == start) + span_at_start = true; + spansToBeMoved.insert(it.key(), span); + } + + if (y == start && span_at_start) + subindex.clear(); + else + it_y = index.erase(it_y); + + if (span_at_start) { + Index::iterator it_start; + if (y == start) + it_start = it_y; + else { + it_start = index.find(-start); + if (it_start == index.end()) + it_start = index.insert(-start, SubIndex()); + } + SubIndex &start_subindex = it_start.value(); + for (SubIndex::iterator it = spansToBeMoved.begin(); it != spansToBeMoved.end(); ++it) + start_subindex.insert(it.key(), it.value()); + } + } else { + if (y == end + 1) { + Index::iterator it_top = index.find(-y + delta); + if (it_top == index.end()) + it_top = index.insert(-y + delta, SubIndex()); + for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) { + Span *span = it.value(); + if (!span->will_be_deleted) + it_top.value().insert(it.key(), span); + ++it; + } + } else { + index.insert(-y + delta, subindex); + } + it_y = index.erase(it_y); + } + } while (it_y != index.begin()); + +#ifdef DEBUG_SPAN_UPDATE + qDebug() << index; + qDebug("Deleted"); + foreach (QSpanCollection::Span *span, spansToBeDeleted) + qDebug() << span << *span; +#endif + qDeleteAll(spansToBeDeleted); +} + +/** \internal +* Updates the span collection after column removal. +*/ +void QSpanCollection::updateRemovedColumns(int start, int end) +{ +#ifdef DEBUG_SPAN_UPDATE + qDebug() << Q_FUNC_INFO; + qDebug() << start << end; + qDebug() << index; +#endif + if (spans.isEmpty()) + return; + + SpanList toBeDeleted; + int delta = end - start + 1; +#ifdef DEBUG_SPAN_UPDATE + qDebug("Before"); +#endif + for (SpanList::iterator it = spans.begin(); it != spans.end(); ) { + Span *span = *it; +#ifdef DEBUG_SPAN_UPDATE + qDebug() << span << *span; +#endif + if (span->m_right < start) { + ++it; + continue; + } + if (span->m_left < start) { + if (span->m_right <= end) + span->m_right = start - 1; + else + span->m_right -= delta; + } else { + if (span->m_right > end) { + if (span->m_left <= end) + span->m_left = start; + else + span->m_left -= delta; + span->m_right -= delta; + } else { + span->will_be_deleted = true; + } + } + if (span->m_top == span->m_bottom && span->m_left == span->m_right) + span->will_be_deleted = true; + if (span->will_be_deleted) { + toBeDeleted.append(span); + it = spans.erase(it); + } else { + ++it; + } + } + +#ifdef DEBUG_SPAN_UPDATE + qDebug("After"); + foreach (QSpanCollection::Span *span, spans) + qDebug() << span << *span; +#endif + if (spans.isEmpty()) { + qDeleteAll(toBeDeleted); + index.clear(); + return; + } + + for (Index::iterator it_y = index.begin(); it_y != index.end(); ) { + int y = -it_y.key(); + if (cleanSpanSubIndex(it_y.value(), y, true)) + it_y = index.erase(it_y); + else + ++it_y; + } + +#ifdef DEBUG_SPAN_UPDATE + qDebug() << index; + qDebug("Deleted"); + foreach (QSpanCollection::Span *span, toBeDeleted) + qDebug() << span << *span; +#endif + qDeleteAll(toBeDeleted); +} + +#ifdef QT_BUILD_INTERNAL +/*! + \internal + Checks whether the span index structure is self-consistent, and consistent with the spans list. +*/ +bool QSpanCollection::checkConsistency() const +{ + for (Index::const_iterator it_y = index.begin(); it_y != index.end(); ++it_y) { + int y = -it_y.key(); + const SubIndex &subIndex = it_y.value(); + for (SubIndex::const_iterator it = subIndex.begin(); it != subIndex.end(); ++it) { + int x = -it.key(); + Span *span = it.value(); + if (!spans.contains(span) || span->left() != x + || y < span->top() || y > span->bottom()) + return false; + } + } + + foreach (const Span *span, spans) { + if (span->width() < 1 || span->height() < 1 + || (span->width() == 1 && span->height() == 1)) + return false; + for (int y = span->top(); y <= span->bottom(); ++y) { + Index::const_iterator it_y = index.find(-y); + if (it_y == index.end()) { + if (y == span->top()) + return false; + else + continue; + } + const SubIndex &subIndex = it_y.value(); + SubIndex::const_iterator it = subIndex.find(-span->left()); + if (it == subIndex.end() || it.value() != span) + return false; + } + } + return true; +} +#endif + +class QTableCornerButton : public QAbstractButton +{ + Q_OBJECT +public: + QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {} + void paintEvent(QPaintEvent*) { + QStyleOptionHeader opt; + opt.init(this); + QStyle::State state = QStyle::State_None; + if (isEnabled()) + state |= QStyle::State_Enabled; + if (isActiveWindow()) + state |= QStyle::State_Active; + if (isDown()) + state |= QStyle::State_Sunken; + opt.state = state; + opt.rect = rect(); + opt.position = QStyleOptionHeader::OnlyOneSection; + QPainter painter(this); + style()->drawControl(QStyle::CE_Header, &opt, &painter, this); + } +}; + +void QTableViewPrivate::init() +{ + Q_Q(QTableView); + + q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed); + + QHeaderView *vertical = new QHeaderView(Qt::Vertical, q); + vertical->setClickable(true); + vertical->setHighlightSections(true); + q->setVerticalHeader(vertical); + + QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q); + horizontal->setClickable(true); + horizontal->setHighlightSections(true); + q->setHorizontalHeader(horizontal); + + tabKeyNavigation = true; + + cornerWidget = new QTableCornerButton(q); + cornerWidget->setFocusPolicy(Qt::NoFocus); + QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll())); +} + +/*! + \internal + Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections. +*/ +void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const +{ + Q_ASSERT(range && range->isValid()); + + int top = range->top(); + int left = range->left(); + int bottom = range->bottom(); + int right = range->right(); + + while (bottom >= top && verticalHeader->isSectionHidden(bottom)) + --bottom; + while (right >= left && horizontalHeader->isSectionHidden(right)) + --right; + + if (top > bottom || left > right) { // everything is hidden + *range = QItemSelectionRange(); + return; + } + + while (verticalHeader->isSectionHidden(top) && top <= bottom) + ++top; + while (horizontalHeader->isSectionHidden(left) && left <= right) + ++left; + + if (top > bottom || left > right) { // everything is hidden + *range = QItemSelectionRange(); + return; + } + + QModelIndex bottomRight = model->index(bottom, right, range->parent()); + QModelIndex topLeft = model->index(top, left, range->parent()); + *range = QItemSelectionRange(topLeft, bottomRight); +} + +/*! + \internal + Sets the span for the cell at (\a row, \a column). +*/ +void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan) +{ + if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0) { + qWarning() << "QTableView::setSpan: invalid span given: (" << row << ',' << column << ',' << rowSpan << ',' << columnSpan << ')'; + return; + } + QSpanCollection::Span *sp = spans.spanAt(column, row); + if (sp) { + if (sp->top() != row || sp->left() != column) { + qWarning() << "QTableView::setSpan: span cannot overlap"; + return; + } + if (rowSpan == 1 && columnSpan == 1) { + rowSpan = columnSpan = 0; + } + const int old_height = sp->height(); + sp->m_bottom = row + rowSpan - 1; + sp->m_right = column + columnSpan - 1; + spans.updateSpan(sp, old_height); + return; + } else if (rowSpan == 1 && columnSpan == 1) { + qWarning() << "QTableView::setSpan: single cell span won't be added"; + return; + } + sp = new QSpanCollection::Span(row, column, rowSpan, columnSpan); + spans.addSpan(sp); +} + +/*! + \internal + Gets the span information for the cell at (\a row, \a column). +*/ +QSpanCollection::Span QTableViewPrivate::span(int row, int column) const +{ + QSpanCollection::Span *sp = spans.spanAt(column, row); + if (sp) + return *sp; + + return QSpanCollection::Span(row, column, 1, 1); +} + +/*! + \internal + Returns the logical index of the last section that's part of the span. +*/ +int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const +{ + int visual = header->visualIndex(logical); + for (int i = 1; i < span; ) { + if (++visual >= header->count()) + break; + logical = header->logicalIndex(visual); + ++i; + } + return logical; +} + +/*! + \internal + Returns the size of the span starting at logical index \a logical + and spanning \a span sections. +*/ +int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const +{ + int endLogical = sectionSpanEndLogical(header, logical, span); + return header->sectionPosition(endLogical) + - header->sectionPosition(logical) + + header->sectionSize(endLogical); +} + +/*! + \internal + Returns true if the section at logical index \a logical is part of the span + starting at logical index \a spanLogical and spanning \a span sections; + otherwise, returns false. +*/ +bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const +{ + if (logical == spanLogical) + return true; // it's the start of the span + int visual = header->visualIndex(spanLogical); + for (int i = 1; i < span; ) { + if (++visual >= header->count()) + break; + spanLogical = header->logicalIndex(visual); + if (logical == spanLogical) + return true; + ++i; + } + return false; +} + +/*! + \internal + Returns the visual rect for the given \a span. +*/ +QRect QTableViewPrivate::visualSpanRect(const QSpanCollection::Span &span) const +{ + Q_Q(const QTableView); + // vertical + int row = span.top(); + int rowp = verticalHeader->sectionViewportPosition(row); + int rowh = rowSpanHeight(row, span.height()); + // horizontal + int column = span.left(); + int colw = columnSpanWidth(column, span.width()); + if (q->isRightToLeft()) + column = span.right(); + int colp = horizontalHeader->sectionViewportPosition(column); + + const int i = showGrid ? 1 : 0; + if (q->isRightToLeft()) + return QRect(colp + i, rowp, colw - i, rowh - i); + return QRect(colp, rowp, colw - i, rowh - i); +} + +/*! + \internal + Draws the spanning cells within rect \a area, and clips them off as + preparation for the main drawing loop. + \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn +*/ +void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter, + const QStyleOptionViewItemV4 &option, QBitArray *drawn, + int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn) +{ + bool alternateBase = false; + QRegion region = viewport->rect(); + + QList visibleSpans; + bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved(); + + if (!sectionMoved) { + visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow), + lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1); + } else { + QSet set; + for(int x = firstVisualColumn; x <= lastVisualColumn; x++) + for(int y = firstVisualRow; y <= lastVisualRow; y++) + set.insert(spans.spanAt(x,y)); + set.remove(0); + visibleSpans = set.toList(); + } + + foreach (QSpanCollection::Span *span, visibleSpans) { + int row = span->top(); + int col = span->left(); + QModelIndex index = model->index(row, col, root); + if (!index.isValid()) + continue; + QRect rect = visualSpanRect(*span); + rect.translate(scrollDelayOffset); + if (!area.intersects(rect)) + continue; + QStyleOptionViewItemV4 opt = option; + opt.rect = rect; + alternateBase = alternatingColors && (span->top() & 1); + if (alternateBase) + opt.features |= QStyleOptionViewItemV2::Alternate; + else + opt.features &= ~QStyleOptionViewItemV2::Alternate; + drawCell(painter, opt, index); + region -= rect; + for (int r = span->top(); r <= span->bottom(); ++r) { + const int vr = visualRow(r); + if (vr < firstVisualRow || vr > lastVisualRow) + continue; + for (int c = span->left(); c <= span->right(); ++c) { + const int vc = visualColumn(c); + if (vc < firstVisualColumn || vc > lastVisualColumn) + continue; + drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1) + + vc - firstVisualColumn); + } + } + + } + painter->setClipRegion(region); +} + +/*! + \internal + Updates spans after row insertion. +*/ +void QTableViewPrivate::_q_updateSpanInsertedRows(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + spans.updateInsertedRows(start, end); +} + +/*! + \internal + Updates spans after column insertion. +*/ +void QTableViewPrivate::_q_updateSpanInsertedColumns(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + spans.updateInsertedColumns(start, end); +} + +/*! + \internal + Updates spans after row removal. +*/ +void QTableViewPrivate::_q_updateSpanRemovedRows(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + spans.updateRemovedRows(start, end); +} + +/*! + \internal + Updates spans after column removal. +*/ +void QTableViewPrivate::_q_updateSpanRemovedColumns(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + spans.updateRemovedColumns(start, end); +} + +/*! + \internal + Draws a table cell. +*/ +void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index) +{ + Q_Q(QTableView); + QStyleOptionViewItemV4 opt = option; + + if (selectionModel && selectionModel->isSelected(index)) + opt.state |= QStyle::State_Selected; + if (index == hover) + opt.state |= QStyle::State_MouseOver; + if (option.state & QStyle::State_Enabled) { + QPalette::ColorGroup cg; + if ((model->flags(index) & Qt::ItemIsEnabled) == 0) { + opt.state &= ~QStyle::State_Enabled; + cg = QPalette::Disabled; + } else { + cg = QPalette::Normal; + } + opt.palette.setCurrentColorGroup(cg); + } + + if (index == q->currentIndex()) { + const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid(); + if (focus) + opt.state |= QStyle::State_HasFocus; + } + + q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q); + + if (const QWidget *widget = editorForIndex(index).editor) { + painter->save(); + painter->setClipRect(widget->geometry()); + q->itemDelegate(index)->paint(painter, opt, index); + painter->restore(); + } else { + q->itemDelegate(index)->paint(painter, opt, index); + } +} + +/*! + \class QTableView + + \brief The QTableView class provides a default model/view + implementation of a table view. + + \ingroup model-view + \ingroup advanced + + + A QTableView implements a table view that displays items from a + model. This class is used to provide standard tables that were + previously provided by the QTable class, but using the more + flexible approach provided by Qt's model/view architecture. + + The QTableView class is one of the \l{Model/View Classes} + and is part of Qt's \l{Model/View Programming}{model/view framework}. + + QTableView implements the interfaces defined by the + QAbstractItemView class to allow it to display data provided by + models derived from the QAbstractItemModel class. + + \section1 Navigation + + You can navigate the cells in the table by clicking on a cell with the + mouse, or by using the arrow keys. Because QTableView enables + \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you + can also hit Tab and Backtab to move from cell to cell. + + \section1 Visual Appearance + + The table has a vertical header that can be obtained using the + verticalHeader() function, and a horizontal header that is available + through the horizontalHeader() function. The height of each row in the + table can be found by using rowHeight(); similarly, the width of + columns can be found using columnWidth(). Since both of these are plain + widgets, you can hide either of them using their hide() functions. + + Rows and columns can be hidden and shown with hideRow(), hideColumn(), + showRow(), and showColumn(). They can be selected with selectRow() + and selectColumn(). The table will show a grid depending on the + \l showGrid property. + + The items shown in a table view, like those in the other item views, are + rendered and edited using standard \l{QItemDelegate}{delegates}. However, + for some tasks it is sometimes useful to be able to insert widgets in a + table instead. Widgets are set for particular indexes with the + \l{QAbstractItemView::}{setIndexWidget()} function, and + later retrieved with \l{QAbstractItemView::}{indexWidget()}. + + \table + \row \o \inlineimage qtableview-resized.png + \o By default, the cells in a table do not expand to fill the available space. + + You can make the cells fill the available space by stretching the last + header section. Access the relevant header using horizontalHeader() + or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection} + property. + + To distribute the available space according to the space requirement of + each column or row, call the view's resizeColumnsToContents() or + resizeRowsToContents() functions. + \endtable + + \section1 Coordinate Systems + + For some specialized forms of tables it is useful to be able to + convert between row and column indexes and widget coordinates. + The rowAt() function provides the y-coordinate within the view of the + specified row; the row index can be used to obtain a corresponding + y-coordinate with rowViewportPosition(). The columnAt() and + columnViewportPosition() functions provide the equivalent conversion + operations between x-coordinates and column indexes. + + \section1 Styles + + QTableView is styled appropriately for each platform. The following images show + how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see + its appearance in other styles. + + \table 100% + \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view + \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view + \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view + \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view. + \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view. + \o A \l{Plastique Style Widget Gallery}{Plastique style} table view. + \endtable + + \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView, + {Chart Example}, {Pixelator Example}, {Table Model Example} +*/ + +/*! + Constructs a table view with a \a parent to represent the data. + + \sa QAbstractItemModel +*/ + +QTableView::QTableView(QWidget *parent) + : QAbstractItemView(*new QTableViewPrivate, parent) +{ + Q_D(QTableView); + d->init(); +} + +/*! + \internal +*/ +QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent) + : QAbstractItemView(dd, parent) +{ + Q_D(QTableView); + d->init(); +} + +/*! + Destroys the table view. +*/ +QTableView::~QTableView() +{ +} + +/*! + \reimp +*/ +void QTableView::setModel(QAbstractItemModel *model) +{ + Q_D(QTableView); + if (model == d->model) + return; + //let's disconnect from the old model + if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) { + disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int))); + disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int))); + disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int))); + disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int))); + } + if (model) { //and connect to the new one + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int))); + connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)), + this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int))); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int))); + connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)), + this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int))); + } + d->verticalHeader->setModel(model); + d->horizontalHeader->setModel(model); + QAbstractItemView::setModel(model); +} + +/*! + \reimp +*/ +void QTableView::setRootIndex(const QModelIndex &index) +{ + Q_D(QTableView); + if (index == d->root) { + viewport()->update(); + return; + } + d->verticalHeader->setRootIndex(index); + d->horizontalHeader->setRootIndex(index); + QAbstractItemView::setRootIndex(index); +} + +/*! + \reimp +*/ +void QTableView::setSelectionModel(QItemSelectionModel *selectionModel) +{ + Q_D(QTableView); + Q_ASSERT(selectionModel); + d->verticalHeader->setSelectionModel(selectionModel); + d->horizontalHeader->setSelectionModel(selectionModel); + QAbstractItemView::setSelectionModel(selectionModel); +} + +/*! + Returns the table view's horizontal header. + + \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData() +*/ +QHeaderView *QTableView::horizontalHeader() const +{ + Q_D(const QTableView); + return d->horizontalHeader; +} + +/*! + Returns the table view's vertical header. + + \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData() +*/ +QHeaderView *QTableView::verticalHeader() const +{ + Q_D(const QTableView); + return d->verticalHeader; +} + +/*! + Sets the widget to use for the horizontal header to \a header. + + \sa horizontalHeader() setVerticalHeader() +*/ +void QTableView::setHorizontalHeader(QHeaderView *header) +{ + Q_D(QTableView); + + if (!header || header == d->horizontalHeader) + return; + if (d->horizontalHeader && d->horizontalHeader->parent() == this) + delete d->horizontalHeader; + d->horizontalHeader = header; + d->horizontalHeader->setParent(this); + if (!d->horizontalHeader->model()) { + d->horizontalHeader->setModel(d->model); + if (d->selectionModel) + d->horizontalHeader->setSelectionModel(d->selectionModel); + } + + connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)), + this, SLOT(columnResized(int,int,int))); + connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)), + this, SLOT(columnMoved(int,int,int))); + connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)), + this, SLOT(columnCountChanged(int,int))); + connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int))); + connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int))); + connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)), + this, SLOT(resizeColumnToContents(int))); + connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries())); + + //update the sorting enabled states on the new header + setSortingEnabled(d->sortingEnabled); +} + +/*! + Sets the widget to use for the vertical header to \a header. + + \sa verticalHeader() setHorizontalHeader() +*/ +void QTableView::setVerticalHeader(QHeaderView *header) +{ + Q_D(QTableView); + + if (!header || header == d->verticalHeader) + return; + if (d->verticalHeader && d->verticalHeader->parent() == this) + delete d->verticalHeader; + d->verticalHeader = header; + d->verticalHeader->setParent(this); + if (!d->verticalHeader->model()) { + d->verticalHeader->setModel(d->model); + if (d->selectionModel) + d->verticalHeader->setSelectionModel(d->selectionModel); + } + + connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)), + this, SLOT(rowResized(int,int,int))); + connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)), + this, SLOT(rowMoved(int,int,int))); + connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)), + this, SLOT(rowCountChanged(int,int))); + connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int))); + connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int))); + connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)), + this, SLOT(resizeRowToContents(int))); + connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries())); +} + +/*! + \internal + + Scroll the contents of the table view by (\a dx, \a dy). +*/ +void QTableView::scrollContentsBy(int dx, int dy) +{ + Q_D(QTableView); + + d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling + + dx = isRightToLeft() ? -dx : dx; + if (dx) { + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { + int oldOffset = d->horizontalHeader->offset(); + if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum()) + d->horizontalHeader->setOffsetToLastSection(); + else + d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value()); + int newOffset = d->horizontalHeader->offset(); + dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset; + } else { + d->horizontalHeader->setOffset(horizontalScrollBar()->value()); + } + } + if (dy) { + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + int oldOffset = d->verticalHeader->offset(); + if (verticalScrollBar()->value() == verticalScrollBar()->maximum()) + d->verticalHeader->setOffsetToLastSection(); + else + d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); + int newOffset = d->verticalHeader->offset(); + dy = oldOffset - newOffset; + } else { + d->verticalHeader->setOffset(verticalScrollBar()->value()); + } + } + d->scrollContentsBy(dx, dy); + + if (d->showGrid) { + //we need to update the first line of the previous top item in the view + //because it has the grid drawn if the header is invisible. + //It is strictly related to what's done at then end of the paintEvent + if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) { + d->viewport->update(0, dy, d->viewport->width(), dy); + } + if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) { + d->viewport->update(dx, 0, dx, d->viewport->height()); + } + } +} + +/*! + \reimp +*/ +QStyleOptionViewItem QTableView::viewOptions() const +{ + QStyleOptionViewItem option = QAbstractItemView::viewOptions(); + option.showDecorationSelected = true; + return option; +} + +/*! + Paints the table on receipt of the given paint event \a event. +*/ +void QTableView::paintEvent(QPaintEvent *event) +{ + Q_D(QTableView); + // setup temp variables for the painting + QStyleOptionViewItemV4 option = d->viewOptionsV4(); + const QPoint offset = d->scrollDelayOffset; + const bool showGrid = d->showGrid; + const int gridSize = showGrid ? 1 : 0; + const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this); + const QColor gridColor = static_cast(gridHint); + const QPen gridPen = QPen(gridColor, 0, d->gridStyle); + const QHeaderView *verticalHeader = d->verticalHeader; + const QHeaderView *horizontalHeader = d->horizontalHeader; + const QStyle::State state = option.state; + const bool alternate = d->alternatingColors; + const bool rightToLeft = isRightToLeft(); + + QPainter painter(d->viewport); + + // if there's nothing to do, clear the area and return + if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate) + return; + + uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1); + uint y = verticalHeader->length() - verticalHeader->offset() - 1; + + const QRegion region = event->region().translated(offset); + const QVector rects = region.rects(); + + //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row. + //same goes for ...VisualColumn + int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0); + int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height()); + if (lastVisualRow == -1) + lastVisualRow = d->model->rowCount(d->root) - 1; + + int firstVisualColumn = horizontalHeader->visualIndexAt(0); + int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width()); + if (rightToLeft) + qSwap(firstVisualColumn, lastVisualColumn); + if (firstVisualColumn == -1) + firstVisualColumn = 0; + if (lastVisualColumn == -1) + lastVisualColumn = horizontalHeader->count() - 1; + + QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1)); + + if (d->hasSpans()) { + d->drawAndClipSpans(region, &painter, option, &drawn, + firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn); + } + + for (int i = 0; i < rects.size(); ++i) { + QRect dirtyArea = rects.at(i); + dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y))); + if (rightToLeft) { + dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x))); + } else { + dirtyArea.setRight(qMin(dirtyArea.right(), int(x))); + } + + // get the horizontal start and end visual sections + int left = horizontalHeader->visualIndexAt(dirtyArea.left()); + int right = horizontalHeader->visualIndexAt(dirtyArea.right()); + if (rightToLeft) + qSwap(left, right); + if (left == -1) left = 0; + if (right == -1) right = horizontalHeader->count() - 1; + + // get the vertical start and end visual sections and if alternate color + int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom()); + if (bottom == -1) bottom = verticalHeader->count() - 1; + int top = 0; + bool alternateBase = false; + if (alternate && verticalHeader->sectionsHidden()) { + uint verticalOffset = verticalHeader->offset(); + int row = verticalHeader->logicalIndex(top); + for (int y = 0; + ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom); + ++top) { + row = verticalHeader->logicalIndex(top); + if (alternate && !verticalHeader->isSectionHidden(row)) + alternateBase = !alternateBase; + } + } else { + top = verticalHeader->visualIndexAt(dirtyArea.top()); + alternateBase = (top & 1) && alternate; + } + if (top == -1 || top > bottom) + continue; + + // Paint each row item + for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) { + int row = verticalHeader->logicalIndex(visualRowIndex); + if (verticalHeader->isSectionHidden(row)) + continue; + int rowY = rowViewportPosition(row); + rowY += offset.y(); + int rowh = rowHeight(row) - gridSize; + + // Paint each column item + for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) { + int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1) + + visualColumnIndex - firstVisualColumn; + + if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit)) + continue; + drawn.setBit(currentBit); + + int col = horizontalHeader->logicalIndex(visualColumnIndex); + if (horizontalHeader->isSectionHidden(col)) + continue; + int colp = columnViewportPosition(col); + colp += offset.x(); + int colw = columnWidth(col) - gridSize; + + const QModelIndex index = d->model->index(row, col, d->root); + if (index.isValid()) { + option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh); + if (alternate) { + if (alternateBase) + option.features |= QStyleOptionViewItemV2::Alternate; + else + option.features &= ~QStyleOptionViewItemV2::Alternate; + } + d->drawCell(&painter, option, index); + } + } + alternateBase = !alternateBase && alternate; + } + + if (showGrid) { + // Find the bottom right (the last rows/coloumns might be hidden) + while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom; + QPen old = painter.pen(); + painter.setPen(gridPen); + // Paint each row + for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) { + int row = verticalHeader->logicalIndex(visualIndex); + if (verticalHeader->isSectionHidden(row)) + continue; + int rowY = rowViewportPosition(row); + rowY += offset.y(); + int rowh = rowHeight(row) - gridSize; + painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh); + } + + // Paint each column + for (int h = left; h <= right; ++h) { + int col = horizontalHeader->logicalIndex(h); + if (horizontalHeader->isSectionHidden(col)) + continue; + int colp = columnViewportPosition(col); + colp += offset.x(); + if (!rightToLeft) + colp += columnWidth(col) - gridSize; + painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom()); + } + + //draw the top & left grid lines if the headers are not visible. + //We do update this line when subsequent scroll happen (see scrollContentsBy) + if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem) + painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0); + if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem) + painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom()); + painter.setPen(old); + } + } + +#ifndef QT_NO_DRAGANDDROP + // Paint the dropIndicator + d->paintDropIndicator(&painter); +#endif +} + +/*! + Returns the index position of the model item corresponding to the + table item at position \a pos in contents coordinates. +*/ +QModelIndex QTableView::indexAt(const QPoint &pos) const +{ + Q_D(const QTableView); + d->executePostedLayout(); + int r = rowAt(pos.y()); + int c = columnAt(pos.x()); + if (r >= 0 && c >= 0) { + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(r, c); + r = span.top(); + c = span.left(); + } + return d->model->index(r, c, d->root); + } + return QModelIndex(); +} + +/*! + Returns the horizontal offset of the items in the table view. + + Note that the table view uses the horizontal header section + positions to determine the positions of columns in the view. + + \sa verticalOffset() +*/ +int QTableView::horizontalOffset() const +{ + Q_D(const QTableView); + return d->horizontalHeader->offset(); +} + +/*! + Returns the vertical offset of the items in the table view. + + Note that the table view uses the vertical header section + positions to determine the positions of rows in the view. + + \sa horizontalOffset() +*/ +int QTableView::verticalOffset() const +{ + Q_D(const QTableView); + return d->verticalHeader->offset(); +} + +/*! + \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) + + Moves the cursor in accordance with the given \a cursorAction, using the + information provided by the \a modifiers. + + \sa QAbstractItemView::CursorAction +*/ +QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) +{ + Q_D(QTableView); + Q_UNUSED(modifiers); + + int bottom = d->model->rowCount(d->root) - 1; + // make sure that bottom is the bottommost *visible* row + while (bottom >= 0 && isRowHidden(d->logicalRow(bottom))) + --bottom; + + int right = d->model->columnCount(d->root) - 1; + + while (right >= 0 && isColumnHidden(d->logicalColumn(right))) + --right; + + if (bottom == -1 || right == -1) + return QModelIndex(); // model is empty + + QModelIndex current = currentIndex(); + + if (!current.isValid()) { + int row = 0; + int column = 0; + while (column < right && isColumnHidden(d->logicalColumn(column))) + ++column; + while (isRowHidden(d->logicalRow(row)) && row < bottom) + ++row; + d->visualCursor = QPoint(column, row); + return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root); + } + + // Update visual cursor if current index has changed. + QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row())); + if (visualCurrent != d->visualCursor) { + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(current.row(), current.column()); + if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom() + || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right()) + d->visualCursor = visualCurrent; + } else { + d->visualCursor = visualCurrent; + } + } + + int visualRow = d->visualCursor.y(); + if (visualRow > bottom) + visualRow = bottom; + Q_ASSERT(visualRow != -1); + int visualColumn = d->visualCursor.x(); + if (visualColumn > right) + visualColumn = right; + Q_ASSERT(visualColumn != -1); + + if (isRightToLeft()) { + if (cursorAction == MoveLeft) + cursorAction = MoveRight; + else if (cursorAction == MoveRight) + cursorAction = MoveLeft; + } + + switch (cursorAction) { + case MoveUp: { + int originalRow = visualRow; +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && visualRow == 0) + visualRow = d->visualRow(model()->rowCount() - 1) + 1; + // FIXME? visualRow = bottom + 1; +#endif + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (r != -1 && d->hasSpans()) { + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualRow = d->visualRow(span.top()); + } + while (visualRow >= 0) { + --visualRow; + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c))) + break; + } + if (visualRow < 0) + visualRow = originalRow; + break; + } + case MoveDown: { + int originalRow = visualRow; + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(current.row(), current.column()); + visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); + } +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && visualRow >= bottom) + visualRow = -1; +#endif + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (r != -1 && d->hasSpans()) { + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); + } + while (visualRow <= bottom) { + ++visualRow; + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c))) + break; + } + if (visualRow > bottom) + visualRow = originalRow; + break; + } + case MovePrevious: + case MoveLeft: { + int originalRow = visualRow; + int originalColumn = visualColumn; + bool firstTime = true; + bool looped = false; + bool wrapped = false; + do { + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (firstTime && c != -1 && d->hasSpans()) { + firstTime = false; + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualColumn = d->visualColumn(span.left()); + } + while (visualColumn >= 0) { + --visualColumn; + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c))) + break; + if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) { + looped = true; + break; + } + } + if (cursorAction == MoveLeft || visualColumn >= 0) + break; + visualColumn = right + 1; + if (visualRow == 0) { + wrapped = true; + visualRow = bottom; + } else { + --visualRow; + } + } while (!looped); + if (visualColumn < 0) + visualColumn = originalColumn; + break; + } + case MoveNext: + case MoveRight: { + int originalRow = visualRow; + int originalColumn = visualColumn; + bool firstTime = true; + bool looped = false; + bool wrapped = false; + do { + int r = d->logicalRow(visualRow); + int c = d->logicalColumn(visualColumn); + if (firstTime && c != -1 && d->hasSpans()) { + firstTime = false; + QSpanCollection::Span span = d->span(r, c); + if (span.width() > 1 || span.height() > 1) + visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); + } + while (visualColumn <= right) { + ++visualColumn; + r = d->logicalRow(visualRow); + c = d->logicalColumn(visualColumn); + if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c))) + break; + if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) { + looped = true; + break; + } + } + if (cursorAction == MoveRight || visualColumn <= right) + break; + visualColumn = -1; + if (visualRow == bottom) { + wrapped = true; + visualRow = 0; + } else { + ++visualRow; + } + } while (!looped); + if (visualColumn > right) + visualColumn = originalColumn; + break; + } + case MoveHome: + visualColumn = 0; + while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn)) + ++visualColumn; + if (modifiers & Qt::ControlModifier) { + visualRow = 0; + while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn)) + ++visualRow; + } + break; + case MoveEnd: + visualColumn = right; + if (modifiers & Qt::ControlModifier) + visualRow = bottom; + break; + case MovePageUp: { + int newRow = rowAt(visualRect(current).top() - d->viewport->height()); + if (newRow == -1) + newRow = d->logicalRow(0); + return d->model->index(newRow, current.column(), d->root); + } + case MovePageDown: { + int newRow = rowAt(visualRect(current).bottom() + d->viewport->height()); + if (newRow == -1) + newRow = d->logicalRow(bottom); + return d->model->index(newRow, current.column(), d->root); + }} + + d->visualCursor = QPoint(visualColumn, visualRow); + int logicalRow = d->logicalRow(visualRow); + int logicalColumn = d->logicalColumn(visualColumn); + if (!d->model->hasIndex(logicalRow, logicalColumn, d->root)) + return QModelIndex(); + + QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root); + if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result)) + return result; + + return QModelIndex(); +} + +/*! + \fn void QTableView::setSelection(const QRect &rect, + QItemSelectionModel::SelectionFlags flags) + + Selects the items within the given \a rect and in accordance with + the specified selection \a flags. +*/ +void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) +{ + Q_D(QTableView); + QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right()) + : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom()))); + QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) : + qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom()))); + if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br)) + return; + + bool verticalMoved = verticalHeader()->sectionsMoved(); + bool horizontalMoved = horizontalHeader()->sectionsMoved(); + + QItemSelection selection; + + if (d->hasSpans()) { + bool expanded; + int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row())); + int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column())); + int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row())); + int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column())); + do { + expanded = false; + foreach (QSpanCollection::Span *it, d->spans.spans) { + const QSpanCollection::Span &span = *it; + int t = d->visualRow(span.top()); + int l = d->visualColumn(span.left()); + int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height())); + int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width())); + if ((t > bottom) || (l > right) || (top > b) || (left > r)) + continue; // no intersect + if (t < top) { + top = t; + expanded = true; + } + if (l < left) { + left = l; + expanded = true; + } + if (b > bottom) { + bottom = b; + expanded = true; + } + if (r > right) { + right = r; + expanded = true; + } + if (expanded) + break; + } + } while (expanded); + for (int horizontal = left; horizontal <= right; ++horizontal) { + int column = d->logicalColumn(horizontal); + for (int vertical = top; vertical <= bottom; ++vertical) { + int row = d->logicalRow(vertical); + QModelIndex index = d->model->index(row, column, d->root); + selection.append(QItemSelectionRange(index)); + } + } + } else if (verticalMoved && horizontalMoved) { + int top = d->visualRow(tl.row()); + int left = d->visualColumn(tl.column()); + int bottom = d->visualRow(br.row()); + int right = d->visualColumn(br.column()); + for (int horizontal = left; horizontal <= right; ++horizontal) { + int column = d->logicalColumn(horizontal); + for (int vertical = top; vertical <= bottom; ++vertical) { + int row = d->logicalRow(vertical); + QModelIndex index = d->model->index(row, column, d->root); + selection.append(QItemSelectionRange(index)); + } + } + } else if (horizontalMoved) { + int left = d->visualColumn(tl.column()); + int right = d->visualColumn(br.column()); + for (int visual = left; visual <= right; ++visual) { + int column = d->logicalColumn(visual); + QModelIndex topLeft = d->model->index(tl.row(), column, d->root); + QModelIndex bottomRight = d->model->index(br.row(), column, d->root); + selection.append(QItemSelectionRange(topLeft, bottomRight)); + } + } else if (verticalMoved) { + int top = d->visualRow(tl.row()); + int bottom = d->visualRow(br.row()); + for (int visual = top; visual <= bottom; ++visual) { + int row = d->logicalRow(visual); + QModelIndex topLeft = d->model->index(row, tl.column(), d->root); + QModelIndex bottomRight = d->model->index(row, br.column(), d->root); + selection.append(QItemSelectionRange(topLeft, bottomRight)); + } + } else { // nothing moved + selection.append(QItemSelectionRange(tl, br)); + } + + d->selectionModel->select(selection, command); +} + +/*! + \internal + + Returns the rectangle from the viewport of the items in the given + \a selection. +*/ +QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const +{ + Q_D(const QTableView); + + if (selection.isEmpty()) + return QRegion(); + + QRegion selectionRegion; + bool verticalMoved = verticalHeader()->sectionsMoved(); + bool horizontalMoved = horizontalHeader()->sectionsMoved(); + + if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) { + for (int i = 0; i < selection.count(); ++i) { + QItemSelectionRange range = selection.at(i); + if (range.parent() != d->root || !range.isValid()) + continue; + for (int r = range.top(); r <= range.bottom(); ++r) + for (int c = range.left(); c <= range.right(); ++c) + selectionRegion += QRegion(visualRect(d->model->index(r, c, d->root))); + } + } else if (horizontalMoved) { + for (int i = 0; i < selection.count(); ++i) { + QItemSelectionRange range = selection.at(i); + if (range.parent() != d->root || !range.isValid()) + continue; + int top = rowViewportPosition(range.top()); + int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom()); + if (top > bottom) + qSwap(top, bottom); + int height = bottom - top; + for (int c = range.left(); c <= range.right(); ++c) + selectionRegion += QRegion(QRect(columnViewportPosition(c), top, + columnWidth(c), height)); + } + } else if (verticalMoved) { + for (int i = 0; i < selection.count(); ++i) { + QItemSelectionRange range = selection.at(i); + if (range.parent() != d->root || !range.isValid()) + continue; + int left = columnViewportPosition(range.left()); + int right = columnViewportPosition(range.right()) + columnWidth(range.right()); + if (left > right) + qSwap(left, right); + int width = right - left; + for (int r = range.top(); r <= range.bottom(); ++r) + selectionRegion += QRegion(QRect(left, rowViewportPosition(r), + width, rowHeight(r))); + } + } else { // nothing moved + const int gridAdjust = showGrid() ? 1 : 0; + for (int i = 0; i < selection.count(); ++i) { + QItemSelectionRange range = selection.at(i); + if (range.parent() != d->root || !range.isValid()) + continue; + d->trimHiddenSelections(&range); + + const int rtop = rowViewportPosition(range.top()); + const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom()); + int rleft; + int rright; + if (isLeftToRight()) { + rleft = columnViewportPosition(range.left()); + rright = columnViewportPosition(range.right()) + columnWidth(range.right()); + } else { + rleft = columnViewportPosition(range.right()); + rright = columnViewportPosition(range.left()) + columnWidth(range.left()); + } + selectionRegion += QRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust)); + if (d->hasSpans()) { + foreach (QSpanCollection::Span *s, + d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) { + if (range.contains(s->top(), s->left(), range.parent())) + selectionRegion += d->visualSpanRect(*s); + } + } + } + } + + return selectionRegion; +} + + +/*! + \reimp +*/ +QModelIndexList QTableView::selectedIndexes() const +{ + Q_D(const QTableView); + QModelIndexList viewSelected; + QModelIndexList modelSelected; + if (d->selectionModel) + modelSelected = d->selectionModel->selectedIndexes(); + for (int i = 0; i < modelSelected.count(); ++i) { + QModelIndex index = modelSelected.at(i); + if (!isIndexHidden(index) && index.parent() == d->root) + viewSelected.append(index); + } + return viewSelected; +} + + +/*! + This slot is called whenever rows are added or deleted. The + previous number of rows is specified by \a oldCount, and the new + number of rows is specified by \a newCount. +*/ +void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ ) +{ + Q_D(QTableView); + updateGeometries(); + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) + d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value()); + else + d->verticalHeader->setOffset(verticalScrollBar()->value()); + d->viewport->update(); +} + +/*! + This slot is called whenever columns are added or deleted. The + previous number of columns is specified by \a oldCount, and the new + number of columns is specified by \a newCount. +*/ +void QTableView::columnCountChanged(int, int) +{ + Q_D(QTableView); + updateGeometries(); + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) + d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value()); + else + d->horizontalHeader->setOffset(horizontalScrollBar()->value()); + d->viewport->update(); +} + +/*! + \reimp +*/ +void QTableView::updateGeometries() +{ + Q_D(QTableView); + if (d->geometryRecursionBlock) + return; + d->geometryRecursionBlock = true; + + int width = 0; + if (!d->verticalHeader->isHidden()) { + width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width()); + width = qMin(width, d->verticalHeader->maximumWidth()); + } + int height = 0; + if (!d->horizontalHeader->isHidden()) { + height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height()); + height = qMin(height, d->horizontalHeader->maximumHeight()); + } + bool reverse = isRightToLeft(); + if (reverse) + setViewportMargins(0, height, width, 0); + else + setViewportMargins(width, height, 0, 0); + + // update headers + + QRect vg = d->viewport->geometry(); + + int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width); + d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height()); + if (d->verticalHeader->isHidden()) + QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries"); + + int horizontalTop = vg.top() - height; + d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height); + if (d->horizontalHeader->isHidden()) + QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries"); + + // update cornerWidget + if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) { + d->cornerWidget->setHidden(true); + } else { + d->cornerWidget->setHidden(false); + d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height); + } + + // update scroll bars + + // ### move this block into the if + QSize vsize = d->viewport->size(); + QSize max = maximumViewportSize(); + uint horizontalLength = d->horizontalHeader->length(); + uint verticalLength = d->verticalHeader->length(); + if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength) + vsize = max; + + // horizontal scroll bar + const int columnCount = d->horizontalHeader->count(); + const int viewportWidth = vsize.width(); + int columnsInViewport = 0; + for (int width = 0, column = columnCount - 1; column >= 0; --column) { + int logical = d->horizontalHeader->logicalIndex(column); + if (!d->horizontalHeader->isSectionHidden(logical)) { + width += d->horizontalHeader->sectionSize(logical); + if (width > viewportWidth) + break; + ++columnsInViewport; + } + } + columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column + + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { + const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount(); + horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport); + horizontalScrollBar()->setPageStep(columnsInViewport); + if (columnsInViewport >= visibleColumns) + d->horizontalHeader->setOffset(0); + horizontalScrollBar()->setSingleStep(1); + } else { // ScrollPerPixel + horizontalScrollBar()->setPageStep(vsize.width()); + horizontalScrollBar()->setRange(0, horizontalLength - vsize.width()); + horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2)); + } + + // vertical scroll bar + const int rowCount = d->verticalHeader->count(); + const int viewportHeight = vsize.height(); + int rowsInViewport = 0; + for (int height = 0, row = rowCount - 1; row >= 0; --row) { + int logical = d->verticalHeader->logicalIndex(row); + if (!d->verticalHeader->isSectionHidden(logical)) { + height += d->verticalHeader->sectionSize(logical); + if (height > viewportHeight) + break; + ++rowsInViewport; + } + } + rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row + + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount(); + verticalScrollBar()->setRange(0, visibleRows - rowsInViewport); + verticalScrollBar()->setPageStep(rowsInViewport); + if (rowsInViewport >= visibleRows) + d->verticalHeader->setOffset(0); + verticalScrollBar()->setSingleStep(1); + } else { // ScrollPerPixel + verticalScrollBar()->setPageStep(vsize.height()); + verticalScrollBar()->setRange(0, verticalLength - vsize.height()); + verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2)); + } + + d->geometryRecursionBlock = false; + QAbstractItemView::updateGeometries(); +} + +/*! + Returns the size hint for the given \a row's height or -1 if there + is no model. + + If you need to set the height of a given row to a fixed value, call + QHeaderView::resizeSection() on the table's vertical header. + + If you reimplement this function in a subclass, note that the value you + return is only used when resizeRowToContents() is called. In that case, + if a larger row height is required by either the vertical header or + the item delegate, that width will be used instead. + + \sa QWidget::sizeHint, verticalHeader() +*/ +int QTableView::sizeHintForRow(int row) const +{ + Q_D(const QTableView); + + if (!model()) + return -1; + + ensurePolished(); + + int left = qMax(0, columnAt(0)); + int right = columnAt(d->viewport->width()); + if (right == -1) // the table don't have enough columns to fill the viewport + right = d->model->columnCount(d->root) - 1; + + QStyleOptionViewItemV4 option = d->viewOptionsV4(); + + int hint = 0; + QModelIndex index; + for (int column = left; column <= right; ++column) { + int logicalColumn = d->horizontalHeader->logicalIndex(column); + if (d->horizontalHeader->isSectionHidden(logicalColumn)) + continue; + index = d->model->index(row, logicalColumn, d->root); + if (d->wrapItemText) {// for wrapping boundaries + option.rect.setY(rowViewportPosition(index.row())); + option.rect.setHeight(rowHeight(index.row())); + option.rect.setX(columnViewportPosition(index.column())); + option.rect.setWidth(columnWidth(index.column())); + } + + QWidget *editor = d->editorForIndex(index).editor; + if (editor && d->persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().height()); + int min = editor->minimumSize().height(); + int max = editor->maximumSize().height(); + hint = qBound(min, hint, max); + } + + hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height()); + } + + return d->showGrid ? hint + 1 : hint; +} + +/*! + Returns the size hint for the given \a column's width or -1 if + there is no model. + + If you need to set the width of a given column to a fixed value, call + QHeaderView::resizeSection() on the table's horizontal header. + + If you reimplement this function in a subclass, note that the value you + return will be used when resizeColumnToContents() or + QHeaderView::resizeSections() is called. If a larger column width is + required by either the horizontal header or the item delegate, the larger + width will be used instead. + + \sa QWidget::sizeHint, horizontalHeader() +*/ +int QTableView::sizeHintForColumn(int column) const +{ + Q_D(const QTableView); + + if (!model()) + return -1; + + ensurePolished(); + + int top = qMax(0, rowAt(0)); + int bottom = rowAt(d->viewport->height()); + if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport + bottom = d->model->rowCount(d->root) - 1; + + QStyleOptionViewItemV4 option = d->viewOptionsV4(); + + int hint = 0; + QModelIndex index; + for (int row = top; row <= bottom; ++row) { + int logicalRow = d->verticalHeader->logicalIndex(row); + if (d->verticalHeader->isSectionHidden(logicalRow)) + continue; + index = d->model->index(logicalRow, column, d->root); + + QWidget *editor = d->editorForIndex(index).editor; + if (editor && d->persistent.contains(editor)) { + hint = qMax(hint, editor->sizeHint().width()); + int min = editor->minimumSize().width(); + int max = editor->maximumSize().width(); + hint = qBound(min, hint, max); + } + + hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width()); + } + + return d->showGrid ? hint + 1 : hint; +} + +/*! + Returns the y-coordinate in contents coordinates of the given \a + row. +*/ +int QTableView::rowViewportPosition(int row) const +{ + Q_D(const QTableView); + return d->verticalHeader->sectionViewportPosition(row); +} + +/*! + Returns the row in which the given y-coordinate, \a y, in contents + coordinates is located. + + \note This function returns -1 if the given coordinate is not valid + (has no row). + + \sa columnAt() +*/ +int QTableView::rowAt(int y) const +{ + Q_D(const QTableView); + return d->verticalHeader->logicalIndexAt(y); +} + +/*! + \since 4.1 + + Sets the height of the given \a row to be \a height. +*/ +void QTableView::setRowHeight(int row, int height) +{ + Q_D(const QTableView); + d->verticalHeader->resizeSection(row, height); +} + +/*! + Returns the height of the given \a row. + + \sa resizeRowToContents(), columnWidth() +*/ +int QTableView::rowHeight(int row) const +{ + Q_D(const QTableView); + return d->verticalHeader->sectionSize(row); +} + +/*! + Returns the x-coordinate in contents coordinates of the given \a + column. +*/ +int QTableView::columnViewportPosition(int column) const +{ + Q_D(const QTableView); + return d->horizontalHeader->sectionViewportPosition(column); +} + +/*! + Returns the column in which the given x-coordinate, \a x, in contents + coordinates is located. + + \note This function returns -1 if the given coordinate is not valid + (has no column). + + \sa rowAt() +*/ +int QTableView::columnAt(int x) const +{ + Q_D(const QTableView); + return d->horizontalHeader->logicalIndexAt(x); +} + +/*! + \since 4.1 + + Sets the width of the given \a column to be \a width. +*/ +void QTableView::setColumnWidth(int column, int width) +{ + Q_D(const QTableView); + d->horizontalHeader->resizeSection(column, width); +} + +/*! + Returns the width of the given \a column. + + \sa resizeColumnToContents(), rowHeight() +*/ +int QTableView::columnWidth(int column) const +{ + Q_D(const QTableView); + return d->horizontalHeader->sectionSize(column); +} + +/*! + Returns true if the given \a row is hidden; otherwise returns false. + + \sa isColumnHidden() +*/ +bool QTableView::isRowHidden(int row) const +{ + Q_D(const QTableView); + return d->verticalHeader->isSectionHidden(row); +} + +/*! + If \a hide is true \a row will be hidden, otherwise it will be shown. + + \sa setColumnHidden() +*/ +void QTableView::setRowHidden(int row, bool hide) +{ + Q_D(QTableView); + if (row < 0 || row >= d->verticalHeader->count()) + return; + d->verticalHeader->setSectionHidden(row, hide); +} + +/*! + Returns true if the given \a column is hidden; otherwise returns false. + + \sa isRowHidden() +*/ +bool QTableView::isColumnHidden(int column) const +{ + Q_D(const QTableView); + return d->horizontalHeader->isSectionHidden(column); +} + +/*! + If \a hide is true the given \a column will be hidden; otherwise it + will be shown. + + \sa setRowHidden() +*/ +void QTableView::setColumnHidden(int column, bool hide) +{ + Q_D(QTableView); + if (column < 0 || column >= d->horizontalHeader->count()) + return; + d->horizontalHeader->setSectionHidden(column, hide); +} + +/*! + \since 4.2 + \property QTableView::sortingEnabled + \brief whether sorting is enabled + + If this property is true, sorting is enabled for the table. If + this property is false, sorting is not enabled. The default value + is false. + + \note. Setting the property to true with setSortingEnabled() + immediately triggers a call to sortByColumn() with the current + sort section and order. + + \sa sortByColumn() +*/ + +/*! + If \a enabled true enables sorting for the table and immediately + trigger a call to sortByColumn() with the current sort section and + order + */ +void QTableView::setSortingEnabled(bool enable) +{ + Q_D(QTableView); + d->sortingEnabled = enable; + horizontalHeader()->setSortIndicatorShown(enable); + if (enable) { + disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)), + this, SLOT(_q_selectColumn(int))); + disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)), + this, SLOT(selectColumn(int))); + connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), + this, SLOT(sortByColumn(int)), Qt::UniqueConnection); + sortByColumn(horizontalHeader()->sortIndicatorSection(), + horizontalHeader()->sortIndicatorOrder()); + } else { + connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), + this, SLOT(_q_selectColumn(int)), Qt::UniqueConnection); + connect(horizontalHeader(), SIGNAL(sectionPressed(int)), + this, SLOT(selectColumn(int)), Qt::UniqueConnection); + disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), + this, SLOT(sortByColumn(int))); + } +} + +bool QTableView::isSortingEnabled() const +{ + Q_D(const QTableView); + return d->sortingEnabled; +} + +/*! + \property QTableView::showGrid + \brief whether the grid is shown + + If this property is true a grid is drawn for the table; if the + property is false, no grid is drawn. The default value is true. +*/ +bool QTableView::showGrid() const +{ + Q_D(const QTableView); + return d->showGrid; +} + +void QTableView::setShowGrid(bool show) +{ + Q_D(QTableView); + if (d->showGrid != show) { + d->showGrid = show; + d->viewport->update(); + } +} + +/*! + \property QTableView::gridStyle + \brief the pen style used to draw the grid. + + This property holds the style used when drawing the grid (see \l{showGrid}). +*/ +Qt::PenStyle QTableView::gridStyle() const +{ + Q_D(const QTableView); + return d->gridStyle; +} + +void QTableView::setGridStyle(Qt::PenStyle style) +{ + Q_D(QTableView); + if (d->gridStyle != style) { + d->gridStyle = style; + d->viewport->update(); + } +} + +/*! + \property QTableView::wordWrap + \brief the item text word-wrapping policy + \since 4.3 + + If this property is true then the item text is wrapped where + necessary at word-breaks; otherwise it is not wrapped at all. + This property is true by default. + + Note that even of wrapping is enabled, the cell will not be + expanded to fit all text. Ellipsis will be inserted according to + the current \l{QAbstractItemView::}{textElideMode}. + +*/ +void QTableView::setWordWrap(bool on) +{ + Q_D(QTableView); + if (d->wrapItemText == on) + return; + d->wrapItemText = on; + QMetaObject::invokeMethod(d->verticalHeader, "resizeSections"); + QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections"); +} + +bool QTableView::wordWrap() const +{ + Q_D(const QTableView); + return d->wrapItemText; +} + +/*! + \property QTableView::cornerButtonEnabled + \brief whether the button in the top-left corner is enabled + \since 4.3 + + If this property is true then button in the top-left corner + of the table view is enabled. Clicking on this button will + select all the cells in the table view. + + This property is true by default. +*/ +void QTableView::setCornerButtonEnabled(bool enable) +{ + Q_D(QTableView); + d->cornerWidget->setEnabled(enable); +} + +bool QTableView::isCornerButtonEnabled() const +{ + Q_D(const QTableView); + return d->cornerWidget->isEnabled(); +} + +/*! + \internal + + Returns the rectangle on the viewport occupied by the given \a + index. + If the index is hidden in the view it will return a null QRect. +*/ +QRect QTableView::visualRect(const QModelIndex &index) const +{ + Q_D(const QTableView); + if (!d->isIndexValid(index) || index.parent() != d->root + || (!d->hasSpans() && isIndexHidden(index))) + return QRect(); + + d->executePostedLayout(); + + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(index.row(), index.column()); + return d->visualSpanRect(span); + } + + int rowp = rowViewportPosition(index.row()); + int rowh = rowHeight(index.row()); + int colp = columnViewportPosition(index.column()); + int colw = columnWidth(index.column()); + + const int i = showGrid() ? 1 : 0; + return QRect(colp, rowp, colw - i, rowh - i); +} + +/*! + \internal + + Makes sure that the given \a item is visible in the table view, + scrolling if necessary. +*/ +void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint) +{ + Q_D(QTableView); + + // check if we really need to do anything + if (!d->isIndexValid(index) + || (d->model->parent(index) != d->root) + || isRowHidden(index.row()) || isColumnHidden(index.column())) + return; + + QSpanCollection::Span span; + if (d->hasSpans()) + span = d->span(index.row(), index.column()); + + // Adjust horizontal position + + int viewportWidth = d->viewport->width(); + int horizontalOffset = d->horizontalHeader->offset(); + int horizontalPosition = d->horizontalHeader->sectionPosition(index.column()); + int horizontalIndex = d->horizontalHeader->visualIndex(index.column()); + int cellWidth = d->hasSpans() + ? d->columnSpanWidth(index.column(), span.width()) + : d->horizontalHeader->sectionSize(index.column()); + + if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) { + + bool positionAtLeft = (horizontalPosition - horizontalOffset < 0); + bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth); + + if (hint == PositionAtCenter || positionAtRight) { + int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth); + int x = cellWidth; + while (horizontalIndex > 0) { + x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1)); + if (x > w) + break; + --horizontalIndex; + } + } + + if (positionAtRight || hint == PositionAtCenter || positionAtLeft) { + int hiddenSections = 0; + if (d->horizontalHeader->sectionsHidden()) { + for (int s = horizontalIndex - 1; s >= 0; --s) { + int column = d->horizontalHeader->logicalIndex(s); + if (d->horizontalHeader->isSectionHidden(column)) + ++hiddenSections; + } + } + horizontalScrollBar()->setValue(horizontalIndex - hiddenSections); + } + + } else { // ScrollPerPixel + if (hint == PositionAtCenter) { + horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2)); + } else { + if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth) + horizontalScrollBar()->setValue(horizontalPosition); + else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth) + horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth); + } + } + + // Adjust vertical position + + int viewportHeight = d->viewport->height(); + int verticalOffset = d->verticalHeader->offset(); + int verticalPosition = d->verticalHeader->sectionPosition(index.row()); + int verticalIndex = d->verticalHeader->visualIndex(index.row()); + int cellHeight = d->hasSpans() + ? d->rowSpanHeight(index.row(), span.height()) + : d->verticalHeader->sectionSize(index.row()); + + if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) { + if (hint == EnsureVisible) + hint = PositionAtTop; + } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) { + if (hint == EnsureVisible) + hint = PositionAtBottom; + } + + if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) { + + if (hint == PositionAtBottom || hint == PositionAtCenter) { + int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight); + int y = cellHeight; + while (verticalIndex > 0) { + int row = d->verticalHeader->logicalIndex(verticalIndex - 1); + y += d->verticalHeader->sectionSize(row); + if (y > h) + break; + --verticalIndex; + } + } + + if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) { + int hiddenSections = 0; + if (d->verticalHeader->sectionsHidden()) { + for (int s = verticalIndex - 1; s >= 0; --s) { + int row = d->verticalHeader->logicalIndex(s); + if (d->verticalHeader->isSectionHidden(row)) + ++hiddenSections; + } + } + verticalScrollBar()->setValue(verticalIndex - hiddenSections); + } + + } else { // ScrollPerPixel + if (hint == PositionAtTop) { + verticalScrollBar()->setValue(verticalPosition); + } else if (hint == PositionAtBottom) { + verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight); + } else if (hint == PositionAtCenter) { + verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2)); + } + } + + update(index); +} + +/*! + This slot is called to change the height of the given \a row. The + old height is specified by \a oldHeight, and the new height by \a + newHeight. + + \sa columnResized() +*/ +void QTableView::rowResized(int row, int, int) +{ + Q_D(QTableView); + d->rowsToUpdate.append(row); + if (d->rowResizeTimerID == 0) + d->rowResizeTimerID = startTimer(0); +} + +/*! + This slot is called to change the width of the given \a column. + The old width is specified by \a oldWidth, and the new width by \a + newWidth. + + \sa rowResized() +*/ +void QTableView::columnResized(int column, int, int) +{ + Q_D(QTableView); + d->columnsToUpdate.append(column); + if (d->columnResizeTimerID == 0) + d->columnResizeTimerID = startTimer(0); +} + +/*! + \reimp + */ +void QTableView::timerEvent(QTimerEvent *event) +{ + Q_D(QTableView); + + if (event->timerId() == d->columnResizeTimerID) { + updateGeometries(); + killTimer(d->columnResizeTimerID); + d->columnResizeTimerID = 0; + + QRect rect; + int viewportHeight = d->viewport->height(); + int viewportWidth = d->viewport->width(); + if (d->hasSpans()) { + rect = QRect(0, 0, viewportWidth, viewportHeight); + } else { + for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) { + int column = d->columnsToUpdate.at(i); + int x = columnViewportPosition(column); + if (isRightToLeft()) + rect |= QRect(0, 0, x + columnWidth(column), viewportHeight); + else + rect |= QRect(x, 0, viewportWidth - x, viewportHeight); + } + } + + d->viewport->update(rect.normalized()); + d->columnsToUpdate.clear(); + } + + if (event->timerId() == d->rowResizeTimerID) { + updateGeometries(); + killTimer(d->rowResizeTimerID); + d->rowResizeTimerID = 0; + + int viewportHeight = d->viewport->height(); + int viewportWidth = d->viewport->width(); + int top; + if (d->hasSpans()) { + top = 0; + } else { + top = viewportHeight; + for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) { + int y = rowViewportPosition(d->rowsToUpdate.at(i)); + top = qMin(top, y); + } + } + + d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top)); + d->rowsToUpdate.clear(); + } + + QAbstractItemView::timerEvent(event); +} + +/*! + This slot is called to change the index of the given \a row in the + table view. The old index is specified by \a oldIndex, and the new + index by \a newIndex. + + \sa columnMoved() +*/ +void QTableView::rowMoved(int, int oldIndex, int newIndex) +{ + Q_D(QTableView); + + updateGeometries(); + int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex); + int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex); + if (d->hasSpans()) { + d->viewport->update(); + } else { + int oldTop = rowViewportPosition(logicalOldIndex); + int newTop = rowViewportPosition(logicalNewIndex); + int oldBottom = oldTop + rowHeight(logicalOldIndex); + int newBottom = newTop + rowHeight(logicalNewIndex); + int top = qMin(oldTop, newTop); + int bottom = qMax(oldBottom, newBottom); + int height = bottom - top; + d->viewport->update(0, top, d->viewport->width(), height); + } +} + +/*! + This slot is called to change the index of the given \a column in + the table view. The old index is specified by \a oldIndex, and + the new index by \a newIndex. + + \sa rowMoved() +*/ +void QTableView::columnMoved(int, int oldIndex, int newIndex) +{ + Q_D(QTableView); + + updateGeometries(); + int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex); + int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex); + if (d->hasSpans()) { + d->viewport->update(); + } else { + int oldLeft = columnViewportPosition(logicalOldIndex); + int newLeft = columnViewportPosition(logicalNewIndex); + int oldRight = oldLeft + columnWidth(logicalOldIndex); + int newRight = newLeft + columnWidth(logicalNewIndex); + int left = qMin(oldLeft, newLeft); + int right = qMax(oldRight, newRight); + int width = right - left; + d->viewport->update(left, 0, width, d->viewport->height()); + } +} + +/*! + Selects the given \a row in the table view if the current + SelectionMode and SelectionBehavior allows rows to be selected. + + \sa selectColumn() +*/ +void QTableView::selectRow(int row) +{ + Q_D(QTableView); + d->selectRow(row, true); +} + +/*! + Selects the given \a column in the table view if the current + SelectionMode and SelectionBehavior allows columns to be selected. + + \sa selectRow() +*/ +void QTableView::selectColumn(int column) +{ + Q_D(QTableView); + d->selectColumn(column, true); +} + +/*! + Hide the given \a row. + + \sa showRow() hideColumn() +*/ +void QTableView::hideRow(int row) +{ + Q_D(QTableView); + d->verticalHeader->hideSection(row); +} + +/*! + Hide the given \a column. + + \sa showColumn() hideRow() +*/ +void QTableView::hideColumn(int column) +{ + Q_D(QTableView); + d->horizontalHeader->hideSection(column); +} + +/*! + Show the given \a row. + + \sa hideRow() showColumn() +*/ +void QTableView::showRow(int row) +{ + Q_D(QTableView); + d->verticalHeader->showSection(row); +} + +/*! + Show the given \a column. + + \sa hideColumn() showRow() +*/ +void QTableView::showColumn(int column) +{ + Q_D(QTableView); + d->horizontalHeader->showSection(column); +} + +/*! + Resizes the given \a row based on the size hints of the delegate + used to render each item in the row. +*/ +void QTableView::resizeRowToContents(int row) +{ + Q_D(QTableView); + int content = sizeHintForRow(row); + int header = d->verticalHeader->sectionSizeHint(row); + d->verticalHeader->resizeSection(row, qMax(content, header)); +} + +/*! + Resizes all rows based on the size hints of the delegate + used to render each item in the rows. +*/ +void QTableView::resizeRowsToContents() +{ + Q_D(QTableView); + d->verticalHeader->resizeSections(QHeaderView::ResizeToContents); +} + +/*! + Resizes the given \a column based on the size hints of the delegate + used to render each item in the column. + + \note Only visible columns will be resized. Reimplement sizeHintForColumn() + to resize hidden columns as well. +*/ +void QTableView::resizeColumnToContents(int column) +{ + Q_D(QTableView); + int content = sizeHintForColumn(column); + int header = d->horizontalHeader->sectionSizeHint(column); + d->horizontalHeader->resizeSection(column, qMax(content, header)); +} + +/*! + Resizes all columns based on the size hints of the delegate + used to render each item in the columns. +*/ +void QTableView::resizeColumnsToContents() +{ + Q_D(QTableView); + d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents); +} + +/*! + \obsolete + \overload + + Sorts the model by the values in the given \a column. +*/ +void QTableView::sortByColumn(int column) +{ + Q_D(QTableView); + if (column == -1) + return; + d->model->sort(column, d->horizontalHeader->sortIndicatorOrder()); +} + +/*! + \since 4.2 + + Sorts the model by the values in the given \a column in the given \a order. + + \sa sortingEnabled + */ +void QTableView::sortByColumn(int column, Qt::SortOrder order) +{ + Q_D(QTableView); + d->horizontalHeader->setSortIndicator(column, order); + sortByColumn(column); +} + +/*! + \internal +*/ +void QTableView::verticalScrollbarAction(int action) +{ + QAbstractItemView::verticalScrollbarAction(action); +} + +/*! + \internal +*/ +void QTableView::horizontalScrollbarAction(int action) +{ + QAbstractItemView::horizontalScrollbarAction(action); +} + +/*! + \reimp +*/ +bool QTableView::isIndexHidden(const QModelIndex &index) const +{ + Q_D(const QTableView); + Q_ASSERT(d->isIndexValid(index)); + if (isRowHidden(index.row()) || isColumnHidden(index.column())) + return true; + if (d->hasSpans()) { + QSpanCollection::Span span = d->span(index.row(), index.column()); + return !((span.top() == index.row()) && (span.left() == index.column())); + } + return false; +} + +/*! + \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount) + \since 4.2 + + Sets the span of the table element at (\a row, \a column) to the number of + rows and columns specified by (\a rowSpanCount, \a columnSpanCount). + + \sa rowSpan(), columnSpan() +*/ +void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan) +{ + Q_D(QTableView); + if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0) + return; + d->setSpan(row, column, rowSpan, columnSpan); + d->viewport->update(); +} + +/*! + \since 4.2 + + Returns the row span of the table element at (\a row, \a column). + The default is 1. + + \sa setSpan(), columnSpan() +*/ +int QTableView::rowSpan(int row, int column) const +{ + Q_D(const QTableView); + return d->rowSpan(row, column); +} + +/*! + \since 4.2 + + Returns the column span of the table element at (\a row, \a + column). The default is 1. + + \sa setSpan(), rowSpan() +*/ +int QTableView::columnSpan(int row, int column) const +{ + Q_D(const QTableView); + return d->columnSpan(row, column); +} + +/*! + \since 4.4 + + Removes all row and column spans in the table view. + + \sa setSpan() +*/ + +void QTableView::clearSpans() +{ + Q_D(QTableView); + d->spans.clear(); + d->viewport->update(); +} + +void QTableViewPrivate::_q_selectRow(int row) +{ + selectRow(row, false); +} + +void QTableViewPrivate::_q_selectColumn(int column) +{ + selectColumn(column, false); +} + +void QTableViewPrivate::selectRow(int row, bool anchor) +{ + Q_Q(QTableView); + + if (q->selectionBehavior() == QTableView::SelectColumns + || (q->selectionMode() == QTableView::SingleSelection + && q->selectionBehavior() == QTableView::SelectItems)) + return; + + if (row >= 0 && row < model->rowCount(root)) { + int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0); + QModelIndex index = model->index(row, column, root); + QItemSelectionModel::SelectionFlags command = q->selectionCommand(index); + selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + if ((anchor && !(command & QItemSelectionModel::Current)) + || (q->selectionMode() == QTableView::SingleSelection)) + rowSectionAnchor = row; + + if (q->selectionMode() != QTableView::SingleSelection + && command.testFlag(QItemSelectionModel::Toggle)) { + if (anchor) + ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index) + ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; + command &= ~QItemSelectionModel::Toggle; + command |= ctrlDragSelectionFlag; + if (!anchor) + command |= QItemSelectionModel::Current; + } + + QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root); + QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root); + if (verticalHeader->sectionsMoved() && tl.row() != br.row()) + q->setSelection(q->visualRect(tl)|q->visualRect(br), command); + else + selectionModel->select(QItemSelection(tl, br), command); + } +} + +void QTableViewPrivate::selectColumn(int column, bool anchor) +{ + Q_Q(QTableView); + + if (q->selectionBehavior() == QTableView::SelectRows + || (q->selectionMode() == QTableView::SingleSelection + && q->selectionBehavior() == QTableView::SelectItems)) + return; + + if (column >= 0 && column < model->columnCount(root)) { + int row = verticalHeader->logicalIndexAt(0); + QModelIndex index = model->index(row, column, root); + QItemSelectionModel::SelectionFlags command = q->selectionCommand(index); + selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + if ((anchor && !(command & QItemSelectionModel::Current)) + || (q->selectionMode() == QTableView::SingleSelection)) + columnSectionAnchor = column; + + if (q->selectionMode() != QTableView::SingleSelection + && command.testFlag(QItemSelectionModel::Toggle)) { + if (anchor) + ctrlDragSelectionFlag = horizontalHeader->selectionModel()->selectedColumns().contains(index) + ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; + command &= ~QItemSelectionModel::Toggle; + command |= ctrlDragSelectionFlag; + if (!anchor) + command |= QItemSelectionModel::Current; + } + + QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root); + QModelIndex br = model->index(model->rowCount(root) - 1, + qMax(columnSectionAnchor, column), root); + if (horizontalHeader->sectionsMoved() && tl.column() != br.column()) + q->setSelection(q->visualRect(tl)|q->visualRect(br), command); + else + selectionModel->select(QItemSelection(tl, br), command); + } +} + +/*! + \reimp + */ +void QTableView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ +#ifndef QT_NO_ACCESSIBILITY + if (QAccessible::isActive()) { + if (current.isValid()) { + int entry = visualIndex(current) + 1; + if (horizontalHeader()) + ++entry; + QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus); + } + } +#endif + QAbstractItemView::currentChanged(current, previous); +} + +/*! + \reimp + */ +void QTableView::selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ +#ifndef QT_NO_ACCESSIBILITY + if (QAccessible::isActive()) { + // ### does not work properly for selection ranges. + QModelIndex sel = selected.indexes().value(0); + if (sel.isValid()) { + int entry = visualIndex(sel); + if (horizontalHeader()) + ++entry; + QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection); + } + QModelIndex desel = deselected.indexes().value(0); + if (desel.isValid()) { + int entry = visualIndex(sel); + if (horizontalHeader()) + ++entry; + QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove); + } + } +#endif + QAbstractItemView::selectionChanged(selected, deselected); +} + +int QTableView::visualIndex(const QModelIndex &index) const +{ + return index.row(); +} + +QT_END_NAMESPACE + +#include "qtableview.moc" + +#include "moc_qtableview.cpp" + +#endif // QT_NO_TABLEVIEW