1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qtableview.h"
44 #ifndef QT_NO_TABLEVIEW
45 #include <qheaderview.h>
46 #include <qitemdelegate.h>
47 #include <qapplication.h>
52 #include <qbitarray.h>
53 #include <qscrollbar.h>
54 #include <qabstractbutton.h>
55 #include <private/qtableview_p.h>
56 #ifndef QT_NO_ACCESSIBILITY
57 #include <qaccessible.h>
63 Add a span to the collection. the collection takes the ownership.
65 void QSpanCollection::addSpan(QSpanCollection::Span *span)
68 Index::iterator it_y = index.lowerBound(-span->top());
69 if (it_y == index.end() || it_y.key() != -span->top()) {
70 //there is no spans that starts with the row in the index, so create a sublist for it.
72 if (it_y != index.end()) {
73 //the previouslist is the list of spans that sarts _before_ the row of the span.
74 // and which may intersect this row.
75 const SubIndex previousList = it_y.value();
76 foreach(Span *s, previousList) {
77 //If a subspans intersect the row, we need to split it into subspans
78 if(s->bottom() >= span->top())
79 sub_index.insert(-s->left(), s);
82 it_y = index.insert(-span->top(), sub_index);
83 //we will insert span to *it_y in the later loop
86 //insert the span as supspan in all the lists that intesects the span
87 while(-it_y.key() <= span->bottom()) {
88 (*it_y).insert(-span->left(), span);
89 if(it_y == index.begin())
97 * Has to be called after the height and width of a span is changed.
99 * old_height is the height before the change
101 * if the size of the span is now 0x0 the span will be deleted.
103 void QSpanCollection::updateSpan(QSpanCollection::Span *span, int old_height)
105 if (old_height < span->height()) {
106 //add the span as subspan in all the lists that intersect the new covered columns
107 Index::iterator it_y = index.lowerBound(-(span->top() + old_height - 1));
108 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
109 while (-it_y.key() <= span->bottom()) {
110 (*it_y).insert(-span->left(), span);
111 if(it_y == index.begin())
115 } else if (old_height > span->height()) {
116 //remove the span from all the subspans lists that intersect the columns not covered anymore
117 Index::iterator it_y = index.lowerBound(-qMax(span->bottom(), span->top())); //qMax useful if height is 0
118 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
119 while (-it_y.key() <= span->top() + old_height -1) {
120 if (-it_y.key() > span->bottom()) {
121 int removed = (*it_y).remove(-span->left());
122 Q_ASSERT(removed == 1); Q_UNUSED(removed);
123 if (it_y->isEmpty()) {
124 it_y = index.erase(it_y);
127 if(it_y == index.begin())
133 if (span->width() == 0 && span->height() == 0) {
134 spans.removeOne(span);
140 * \return a spans that spans over cell x,y (column,row) or 0 if there is none.
142 QSpanCollection::Span *QSpanCollection::spanAt(int x, int y) const
144 Index::const_iterator it_y = index.lowerBound(-y);
145 if (it_y == index.end())
147 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
148 if (it_x == (*it_y).end())
151 if (span->right() >= x && span->bottom() >= y)
158 * remove and deletes all spans inside the collection
160 void QSpanCollection::clear()
168 * return a list to all the spans that spans over cells in the given rectangle
170 QList<QSpanCollection::Span *> QSpanCollection::spansInRect(int x, int y, int w, int h) const
173 Index::const_iterator it_y = index.lowerBound(-y);
174 if(it_y == index.end())
176 while(-it_y.key() <= y + h) {
177 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
178 if (it_x == (*it_y).end())
180 while(-it_x.key() <= x + w) {
182 if (s->bottom() >= y && s->right() >= x)
184 if (it_x == (*it_y).begin())
188 if(it_y == index.begin())
192 return list.toList();
195 #undef DEBUG_SPAN_UPDATE
197 #ifdef DEBUG_SPAN_UPDATE
198 QDebug operator<<(QDebug str, const QSpanCollection::Span &span)
200 str << "(" << span.top() << "," << span.left() << "," << span.bottom() << "," << span.right() << ")";
206 * Updates the span collection after row insertion.
208 void QSpanCollection::updateInsertedRows(int start, int end)
210 #ifdef DEBUG_SPAN_UPDATE
211 qDebug() << Q_FUNC_INFO;
212 qDebug() << start << end;
218 int delta = end - start + 1;
219 #ifdef DEBUG_SPAN_UPDATE
222 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
224 #ifdef DEBUG_SPAN_UPDATE
225 qDebug() << span << *span;
227 if (span->m_bottom < start)
229 if (span->m_top >= start)
230 span->m_top += delta;
231 span->m_bottom += delta;
234 #ifdef DEBUG_SPAN_UPDATE
236 foreach (QSpanCollection::Span *span, spans)
237 qDebug() << span << *span;
240 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
247 index.insert(-y - delta, it_y.value());
248 it_y = index.erase(it_y);
250 #ifdef DEBUG_SPAN_UPDATE
256 * Updates the span collection after column insertion.
258 void QSpanCollection::updateInsertedColumns(int start, int end)
260 #ifdef DEBUG_SPAN_UPDATE
261 qDebug() << Q_FUNC_INFO;
262 qDebug() << start << end;
268 int delta = end - start + 1;
269 #ifdef DEBUG_SPAN_UPDATE
272 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
274 #ifdef DEBUG_SPAN_UPDATE
275 qDebug() << span << *span;
277 if (span->m_right < start)
279 if (span->m_left >= start)
280 span->m_left += delta;
281 span->m_right += delta;
284 #ifdef DEBUG_SPAN_UPDATE
286 foreach (QSpanCollection::Span *span, spans)
287 qDebug() << span << *span;
290 for (Index::iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
291 SubIndex &subindex = it_y.value();
292 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
298 subindex.insert(-x - delta, it.value());
299 it = subindex.erase(it);
302 #ifdef DEBUG_SPAN_UPDATE
308 * Cleans a subindex from to be deleted spans. The update argument is used
309 * to move the spans inside the subindex, in case their anchor changed.
310 * \return true if no span in this subindex starts at y, and should thus be deleted.
312 bool QSpanCollection::cleanSpanSubIndex(QSpanCollection::SubIndex &subindex, int y, bool update)
314 if (subindex.isEmpty())
317 bool should_be_deleted = true;
318 SubIndex::iterator it = subindex.end();
322 Span *span = it.value();
323 if (span->will_be_deleted) {
324 it = subindex.erase(it);
327 if (update && span->m_left != x) {
328 subindex.insert(-span->m_left, span);
329 it = subindex.erase(it);
331 if (should_be_deleted && span->m_top == y)
332 should_be_deleted = false;
333 } while (it != subindex.begin());
335 return should_be_deleted;
339 * Updates the span collection after row removal.
341 void QSpanCollection::updateRemovedRows(int start, int end)
343 #ifdef DEBUG_SPAN_UPDATE
344 qDebug() << Q_FUNC_INFO;
345 qDebug() << start << end;
351 SpanList spansToBeDeleted;
352 int delta = end - start + 1;
353 #ifdef DEBUG_SPAN_UPDATE
356 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
358 #ifdef DEBUG_SPAN_UPDATE
359 qDebug() << span << *span;
361 if (span->m_bottom < start) {
365 if (span->m_top < start) {
366 if (span->m_bottom <= end)
367 span->m_bottom = start - 1;
369 span->m_bottom -= delta;
371 if (span->m_bottom > end) {
372 if (span->m_top <= end)
375 span->m_top -= delta;
376 span->m_bottom -= delta;
378 span->will_be_deleted = true;
381 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
382 span->will_be_deleted = true;
383 if (span->will_be_deleted) {
384 spansToBeDeleted.append(span);
385 it = spans.erase(it);
391 #ifdef DEBUG_SPAN_UPDATE
393 foreach (QSpanCollection::Span *span, spans)
394 qDebug() << span << *span;
396 if (spans.isEmpty()) {
397 qDeleteAll(spansToBeDeleted);
402 Index::iterator it_y = index.end();
406 SubIndex &subindex = it_y.value();
408 if (cleanSpanSubIndex(subindex, y))
409 it_y = index.erase(it_y);
410 } else if (y >= start && y <= end) {
411 bool span_at_start = false;
412 SubIndex spansToBeMoved;
413 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ++it) {
414 Span *span = it.value();
415 if (span->will_be_deleted)
417 if (!span_at_start && span->m_top == start)
418 span_at_start = true;
419 spansToBeMoved.insert(it.key(), span);
422 if (y == start && span_at_start)
425 it_y = index.erase(it_y);
428 Index::iterator it_start;
432 it_start = index.find(-start);
433 if (it_start == index.end())
434 it_start = index.insert(-start, SubIndex());
436 SubIndex &start_subindex = it_start.value();
437 for (SubIndex::iterator it = spansToBeMoved.begin(); it != spansToBeMoved.end(); ++it)
438 start_subindex.insert(it.key(), it.value());
442 Index::iterator it_top = index.find(-y + delta);
443 if (it_top == index.end())
444 it_top = index.insert(-y + delta, SubIndex());
445 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
446 Span *span = it.value();
447 if (!span->will_be_deleted)
448 it_top.value().insert(it.key(), span);
452 index.insert(-y + delta, subindex);
454 it_y = index.erase(it_y);
456 } while (it_y != index.begin());
458 #ifdef DEBUG_SPAN_UPDATE
461 foreach (QSpanCollection::Span *span, spansToBeDeleted)
462 qDebug() << span << *span;
464 qDeleteAll(spansToBeDeleted);
468 * Updates the span collection after column removal.
470 void QSpanCollection::updateRemovedColumns(int start, int end)
472 #ifdef DEBUG_SPAN_UPDATE
473 qDebug() << Q_FUNC_INFO;
474 qDebug() << start << end;
480 SpanList toBeDeleted;
481 int delta = end - start + 1;
482 #ifdef DEBUG_SPAN_UPDATE
485 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
487 #ifdef DEBUG_SPAN_UPDATE
488 qDebug() << span << *span;
490 if (span->m_right < start) {
494 if (span->m_left < start) {
495 if (span->m_right <= end)
496 span->m_right = start - 1;
498 span->m_right -= delta;
500 if (span->m_right > end) {
501 if (span->m_left <= end)
502 span->m_left = start;
504 span->m_left -= delta;
505 span->m_right -= delta;
507 span->will_be_deleted = true;
510 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
511 span->will_be_deleted = true;
512 if (span->will_be_deleted) {
513 toBeDeleted.append(span);
514 it = spans.erase(it);
520 #ifdef DEBUG_SPAN_UPDATE
522 foreach (QSpanCollection::Span *span, spans)
523 qDebug() << span << *span;
525 if (spans.isEmpty()) {
526 qDeleteAll(toBeDeleted);
531 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
533 if (cleanSpanSubIndex(it_y.value(), y, true))
534 it_y = index.erase(it_y);
539 #ifdef DEBUG_SPAN_UPDATE
542 foreach (QSpanCollection::Span *span, toBeDeleted)
543 qDebug() << span << *span;
545 qDeleteAll(toBeDeleted);
548 #ifdef QT_BUILD_INTERNAL
551 Checks whether the span index structure is self-consistent, and consistent with the spans list.
553 bool QSpanCollection::checkConsistency() const
555 for (Index::const_iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
557 const SubIndex &subIndex = it_y.value();
558 for (SubIndex::const_iterator it = subIndex.begin(); it != subIndex.end(); ++it) {
560 Span *span = it.value();
561 if (!spans.contains(span) || span->left() != x
562 || y < span->top() || y > span->bottom())
567 foreach (const Span *span, spans) {
568 if (span->width() < 1 || span->height() < 1
569 || (span->width() == 1 && span->height() == 1))
571 for (int y = span->top(); y <= span->bottom(); ++y) {
572 Index::const_iterator it_y = index.find(-y);
573 if (it_y == index.end()) {
574 if (y == span->top())
579 const SubIndex &subIndex = it_y.value();
580 SubIndex::const_iterator it = subIndex.find(-span->left());
581 if (it == subIndex.end() || it.value() != span)
589 class QTableCornerButton : public QAbstractButton
593 QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
594 void paintEvent(QPaintEvent*) {
595 QStyleOptionHeader opt;
597 QStyle::State state = QStyle::State_None;
599 state |= QStyle::State_Enabled;
600 if (isActiveWindow())
601 state |= QStyle::State_Active;
603 state |= QStyle::State_Sunken;
606 opt.position = QStyleOptionHeader::OnlyOneSection;
607 QPainter painter(this);
608 style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
612 void QTableViewPrivate::init()
616 q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed);
618 QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
619 vertical->setClickable(true);
620 vertical->setHighlightSections(true);
621 q->setVerticalHeader(vertical);
623 QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
624 horizontal->setClickable(true);
625 horizontal->setHighlightSections(true);
626 q->setHorizontalHeader(horizontal);
628 tabKeyNavigation = true;
630 cornerWidget = new QTableCornerButton(q);
631 cornerWidget->setFocusPolicy(Qt::NoFocus);
632 QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll()));
637 Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections.
639 void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
641 Q_ASSERT(range && range->isValid());
643 int top = range->top();
644 int left = range->left();
645 int bottom = range->bottom();
646 int right = range->right();
648 while (bottom >= top && verticalHeader->isSectionHidden(bottom))
650 while (right >= left && horizontalHeader->isSectionHidden(right))
653 if (top > bottom || left > right) { // everything is hidden
654 *range = QItemSelectionRange();
658 while (verticalHeader->isSectionHidden(top) && top <= bottom)
660 while (horizontalHeader->isSectionHidden(left) && left <= right)
663 if (top > bottom || left > right) { // everything is hidden
664 *range = QItemSelectionRange();
668 QModelIndex bottomRight = model->index(bottom, right, range->parent());
669 QModelIndex topLeft = model->index(top, left, range->parent());
670 *range = QItemSelectionRange(topLeft, bottomRight);
675 Sets the span for the cell at (\a row, \a column).
677 void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
679 if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0) {
680 qWarning() << "QTableView::setSpan: invalid span given: (" << row << ',' << column << ',' << rowSpan << ',' << columnSpan << ')';
683 QSpanCollection::Span *sp = spans.spanAt(column, row);
685 if (sp->top() != row || sp->left() != column) {
686 qWarning() << "QTableView::setSpan: span cannot overlap";
689 if (rowSpan == 1 && columnSpan == 1) {
690 rowSpan = columnSpan = 0;
692 const int old_height = sp->height();
693 sp->m_bottom = row + rowSpan - 1;
694 sp->m_right = column + columnSpan - 1;
695 spans.updateSpan(sp, old_height);
697 } else if (rowSpan == 1 && columnSpan == 1) {
698 qWarning() << "QTableView::setSpan: single cell span won't be added";
701 sp = new QSpanCollection::Span(row, column, rowSpan, columnSpan);
707 Gets the span information for the cell at (\a row, \a column).
709 QSpanCollection::Span QTableViewPrivate::span(int row, int column) const
711 QSpanCollection::Span *sp = spans.spanAt(column, row);
715 return QSpanCollection::Span(row, column, 1, 1);
720 Returns the logical index of the last section that's part of the span.
722 int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
724 int visual = header->visualIndex(logical);
725 for (int i = 1; i < span; ) {
726 if (++visual >= header->count())
728 logical = header->logicalIndex(visual);
736 Returns the size of the span starting at logical index \a logical
737 and spanning \a span sections.
739 int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const
741 int endLogical = sectionSpanEndLogical(header, logical, span);
742 return header->sectionPosition(endLogical)
743 - header->sectionPosition(logical)
744 + header->sectionSize(endLogical);
749 Returns true if the section at logical index \a logical is part of the span
750 starting at logical index \a spanLogical and spanning \a span sections;
751 otherwise, returns false.
753 bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
755 if (logical == spanLogical)
756 return true; // it's the start of the span
757 int visual = header->visualIndex(spanLogical);
758 for (int i = 1; i < span; ) {
759 if (++visual >= header->count())
761 spanLogical = header->logicalIndex(visual);
762 if (logical == spanLogical)
771 Returns the visual rect for the given \a span.
773 QRect QTableViewPrivate::visualSpanRect(const QSpanCollection::Span &span) const
775 Q_Q(const QTableView);
777 int row = span.top();
778 int rowp = verticalHeader->sectionViewportPosition(row);
779 int rowh = rowSpanHeight(row, span.height());
781 int column = span.left();
782 int colw = columnSpanWidth(column, span.width());
783 if (q->isRightToLeft())
784 column = span.right();
785 int colp = horizontalHeader->sectionViewportPosition(column);
787 const int i = showGrid ? 1 : 0;
788 if (q->isRightToLeft())
789 return QRect(colp + i, rowp, colw - i, rowh - i);
790 return QRect(colp, rowp, colw - i, rowh - i);
795 Draws the spanning cells within rect \a area, and clips them off as
796 preparation for the main drawing loop.
797 \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn
799 void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter,
800 const QStyleOptionViewItemV4 &option, QBitArray *drawn,
801 int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
803 bool alternateBase = false;
804 QRegion region = viewport->rect();
806 QList<QSpanCollection::Span *> visibleSpans;
807 bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved();
810 visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow),
811 lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1);
813 QSet<QSpanCollection::Span *> set;
814 for(int x = firstVisualColumn; x <= lastVisualColumn; x++)
815 for(int y = firstVisualRow; y <= lastVisualRow; y++)
816 set.insert(spans.spanAt(x,y));
818 visibleSpans = set.toList();
821 foreach (QSpanCollection::Span *span, visibleSpans) {
822 int row = span->top();
823 int col = span->left();
824 QModelIndex index = model->index(row, col, root);
825 if (!index.isValid())
827 QRect rect = visualSpanRect(*span);
828 rect.translate(scrollDelayOffset);
829 if (!area.intersects(rect))
831 QStyleOptionViewItemV4 opt = option;
833 alternateBase = alternatingColors && (span->top() & 1);
835 opt.features |= QStyleOptionViewItemV2::Alternate;
837 opt.features &= ~QStyleOptionViewItemV2::Alternate;
838 drawCell(painter, opt, index);
840 for (int r = span->top(); r <= span->bottom(); ++r) {
841 const int vr = visualRow(r);
842 if (vr < firstVisualRow || vr > lastVisualRow)
844 for (int c = span->left(); c <= span->right(); ++c) {
845 const int vc = visualColumn(c);
846 if (vc < firstVisualColumn || vc > lastVisualColumn)
848 drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
849 + vc - firstVisualColumn);
854 painter->setClipRegion(region);
859 Updates spans after row insertion.
861 void QTableViewPrivate::_q_updateSpanInsertedRows(const QModelIndex &parent, int start, int end)
864 spans.updateInsertedRows(start, end);
869 Updates spans after column insertion.
871 void QTableViewPrivate::_q_updateSpanInsertedColumns(const QModelIndex &parent, int start, int end)
874 spans.updateInsertedColumns(start, end);
879 Updates spans after row removal.
881 void QTableViewPrivate::_q_updateSpanRemovedRows(const QModelIndex &parent, int start, int end)
884 spans.updateRemovedRows(start, end);
889 Updates spans after column removal.
891 void QTableViewPrivate::_q_updateSpanRemovedColumns(const QModelIndex &parent, int start, int end)
894 spans.updateRemovedColumns(start, end);
901 void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index)
904 QStyleOptionViewItemV4 opt = option;
906 if (selectionModel && selectionModel->isSelected(index))
907 opt.state |= QStyle::State_Selected;
909 opt.state |= QStyle::State_MouseOver;
910 if (option.state & QStyle::State_Enabled) {
911 QPalette::ColorGroup cg;
912 if ((model->flags(index) & Qt::ItemIsEnabled) == 0) {
913 opt.state &= ~QStyle::State_Enabled;
914 cg = QPalette::Disabled;
916 cg = QPalette::Normal;
918 opt.palette.setCurrentColorGroup(cg);
921 if (index == q->currentIndex()) {
922 const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
924 opt.state |= QStyle::State_HasFocus;
927 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);
929 q->itemDelegate(index)->paint(painter, opt, index);
935 \brief The QTableView class provides a default model/view
936 implementation of a table view.
942 A QTableView implements a table view that displays items from a
943 model. This class is used to provide standard tables that were
944 previously provided by the QTable class, but using the more
945 flexible approach provided by Qt's model/view architecture.
947 The QTableView class is one of the \l{Model/View Classes}
948 and is part of Qt's \l{Model/View Programming}{model/view framework}.
950 QTableView implements the interfaces defined by the
951 QAbstractItemView class to allow it to display data provided by
952 models derived from the QAbstractItemModel class.
956 You can navigate the cells in the table by clicking on a cell with the
957 mouse, or by using the arrow keys. Because QTableView enables
958 \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you
959 can also hit Tab and Backtab to move from cell to cell.
961 \section1 Visual Appearance
963 The table has a vertical header that can be obtained using the
964 verticalHeader() function, and a horizontal header that is available
965 through the horizontalHeader() function. The height of each row in the
966 table can be found by using rowHeight(); similarly, the width of
967 columns can be found using columnWidth(). Since both of these are plain
968 widgets, you can hide either of them using their hide() functions.
970 Rows and columns can be hidden and shown with hideRow(), hideColumn(),
971 showRow(), and showColumn(). They can be selected with selectRow()
972 and selectColumn(). The table will show a grid depending on the
973 \l showGrid property.
975 The items shown in a table view, like those in the other item views, are
976 rendered and edited using standard \l{QItemDelegate}{delegates}. However,
977 for some tasks it is sometimes useful to be able to insert widgets in a
978 table instead. Widgets are set for particular indexes with the
979 \l{QAbstractItemView::}{setIndexWidget()} function, and
980 later retrieved with \l{QAbstractItemView::}{indexWidget()}.
983 \row \o \inlineimage qtableview-resized.png
984 \o By default, the cells in a table do not expand to fill the available space.
986 You can make the cells fill the available space by stretching the last
987 header section. Access the relevant header using horizontalHeader()
988 or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection}
991 To distribute the available space according to the space requirement of
992 each column or row, call the view's resizeColumnsToContents() or
993 resizeRowsToContents() functions.
996 \section1 Coordinate Systems
998 For some specialized forms of tables it is useful to be able to
999 convert between row and column indexes and widget coordinates.
1000 The rowAt() function provides the y-coordinate within the view of the
1001 specified row; the row index can be used to obtain a corresponding
1002 y-coordinate with rowViewportPosition(). The columnAt() and
1003 columnViewportPosition() functions provide the equivalent conversion
1004 operations between x-coordinates and column indexes.
1008 QTableView is styled appropriately for each platform. The following images show
1009 how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see
1010 its appearance in other styles.
1013 \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view
1014 \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view
1015 \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view
1016 \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view.
1017 \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view.
1018 \o A \l{Plastique Style Widget Gallery}{Plastique style} table view.
1021 \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
1022 {Chart Example}, {Pixelator Example}, {Table Model Example}
1026 Constructs a table view with a \a parent to represent the data.
1028 \sa QAbstractItemModel
1031 QTableView::QTableView(QWidget *parent)
1032 : QAbstractItemView(*new QTableViewPrivate, parent)
1041 QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent)
1042 : QAbstractItemView(dd, parent)
1049 Destroys the table view.
1051 QTableView::~QTableView()
1058 void QTableView::setModel(QAbstractItemModel *model)
1061 if (model == d->model)
1063 //let's disconnect from the old model
1064 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
1065 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1066 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1067 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1068 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1069 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1070 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1071 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1072 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1074 if (model) { //and connect to the new one
1075 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1076 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1077 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1078 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1079 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1080 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1081 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1082 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1084 d->verticalHeader->setModel(model);
1085 d->horizontalHeader->setModel(model);
1086 QAbstractItemView::setModel(model);
1092 void QTableView::setRootIndex(const QModelIndex &index)
1095 if (index == d->root) {
1096 viewport()->update();
1099 d->verticalHeader->setRootIndex(index);
1100 d->horizontalHeader->setRootIndex(index);
1101 QAbstractItemView::setRootIndex(index);
1107 void QTableView::doItemsLayout()
1110 QAbstractItemView::doItemsLayout();
1111 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem)
1112 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1114 d->verticalHeader->setOffset(verticalScrollBar()->value());
1115 if (!d->verticalHeader->updatesEnabled())
1116 d->verticalHeader->setUpdatesEnabled(true);
1122 void QTableView::setSelectionModel(QItemSelectionModel *selectionModel)
1125 Q_ASSERT(selectionModel);
1126 d->verticalHeader->setSelectionModel(selectionModel);
1127 d->horizontalHeader->setSelectionModel(selectionModel);
1128 QAbstractItemView::setSelectionModel(selectionModel);
1132 Returns the table view's horizontal header.
1134 \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData()
1136 QHeaderView *QTableView::horizontalHeader() const
1138 Q_D(const QTableView);
1139 return d->horizontalHeader;
1143 Returns the table view's vertical header.
1145 \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData()
1147 QHeaderView *QTableView::verticalHeader() const
1149 Q_D(const QTableView);
1150 return d->verticalHeader;
1154 Sets the widget to use for the horizontal header to \a header.
1156 \sa horizontalHeader() setVerticalHeader()
1158 void QTableView::setHorizontalHeader(QHeaderView *header)
1162 if (!header || header == d->horizontalHeader)
1164 if (d->horizontalHeader && d->horizontalHeader->parent() == this)
1165 delete d->horizontalHeader;
1166 d->horizontalHeader = header;
1167 d->horizontalHeader->setParent(this);
1168 if (!d->horizontalHeader->model()) {
1169 d->horizontalHeader->setModel(d->model);
1170 if (d->selectionModel)
1171 d->horizontalHeader->setSelectionModel(d->selectionModel);
1174 connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)),
1175 this, SLOT(columnResized(int,int,int)));
1176 connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)),
1177 this, SLOT(columnMoved(int,int,int)));
1178 connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)),
1179 this, SLOT(columnCountChanged(int,int)));
1180 connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
1181 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int)));
1182 connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1183 this, SLOT(resizeColumnToContents(int)));
1184 connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1186 //update the sorting enabled states on the new header
1187 setSortingEnabled(d->sortingEnabled);
1191 Sets the widget to use for the vertical header to \a header.
1193 \sa verticalHeader() setHorizontalHeader()
1195 void QTableView::setVerticalHeader(QHeaderView *header)
1199 if (!header || header == d->verticalHeader)
1201 if (d->verticalHeader && d->verticalHeader->parent() == this)
1202 delete d->verticalHeader;
1203 d->verticalHeader = header;
1204 d->verticalHeader->setParent(this);
1205 if (!d->verticalHeader->model()) {
1206 d->verticalHeader->setModel(d->model);
1207 if (d->selectionModel)
1208 d->verticalHeader->setSelectionModel(d->selectionModel);
1211 connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)),
1212 this, SLOT(rowResized(int,int,int)));
1213 connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)),
1214 this, SLOT(rowMoved(int,int,int)));
1215 connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)),
1216 this, SLOT(rowCountChanged(int,int)));
1217 connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
1218 connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int)));
1219 connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1220 this, SLOT(resizeRowToContents(int)));
1221 connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1227 Scroll the contents of the table view by (\a dx, \a dy).
1229 void QTableView::scrollContentsBy(int dx, int dy)
1233 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
1235 dx = isRightToLeft() ? -dx : dx;
1237 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
1238 int oldOffset = d->horizontalHeader->offset();
1239 if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
1240 d->horizontalHeader->setOffsetToLastSection();
1242 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
1243 int newOffset = d->horizontalHeader->offset();
1244 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
1246 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
1250 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1251 int oldOffset = d->verticalHeader->offset();
1252 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
1253 d->verticalHeader->setOffsetToLastSection();
1255 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1256 int newOffset = d->verticalHeader->offset();
1257 dy = oldOffset - newOffset;
1259 d->verticalHeader->setOffset(verticalScrollBar()->value());
1262 d->scrollContentsBy(dx, dy);
1265 //we need to update the first line of the previous top item in the view
1266 //because it has the grid drawn if the header is invisible.
1267 //It is strictly related to what's done at then end of the paintEvent
1268 if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) {
1269 d->viewport->update(0, dy, d->viewport->width(), dy);
1271 if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) {
1272 d->viewport->update(dx, 0, dx, d->viewport->height());
1280 QStyleOptionViewItem QTableView::viewOptions() const
1282 QStyleOptionViewItem option = QAbstractItemView::viewOptions();
1283 option.showDecorationSelected = true;
1288 Paints the table on receipt of the given paint event \a event.
1290 void QTableView::paintEvent(QPaintEvent *event)
1293 // setup temp variables for the painting
1294 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1295 const QPoint offset = d->scrollDelayOffset;
1296 const bool showGrid = d->showGrid;
1297 const int gridSize = showGrid ? 1 : 0;
1298 const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
1299 const QColor gridColor = static_cast<QRgb>(gridHint);
1300 const QPen gridPen = QPen(gridColor, 0, d->gridStyle);
1301 const QHeaderView *verticalHeader = d->verticalHeader;
1302 const QHeaderView *horizontalHeader = d->horizontalHeader;
1303 const bool alternate = d->alternatingColors;
1304 const bool rightToLeft = isRightToLeft();
1306 QPainter painter(d->viewport);
1308 // if there's nothing to do, clear the area and return
1309 if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
1312 uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
1313 uint y = verticalHeader->length() - verticalHeader->offset() - 1;
1315 const QRegion region = event->region().translated(offset);
1316 const QVector<QRect> rects = region.rects();
1318 //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
1319 //same goes for ...VisualColumn
1320 int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
1321 int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height());
1322 if (lastVisualRow == -1)
1323 lastVisualRow = d->model->rowCount(d->root) - 1;
1325 int firstVisualColumn = horizontalHeader->visualIndexAt(0);
1326 int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width());
1328 qSwap(firstVisualColumn, lastVisualColumn);
1329 if (firstVisualColumn == -1)
1330 firstVisualColumn = 0;
1331 if (lastVisualColumn == -1)
1332 lastVisualColumn = horizontalHeader->count() - 1;
1334 QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
1336 if (d->hasSpans()) {
1337 d->drawAndClipSpans(region, &painter, option, &drawn,
1338 firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
1341 for (int i = 0; i < rects.size(); ++i) {
1342 QRect dirtyArea = rects.at(i);
1343 dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
1345 dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
1347 dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
1350 // get the horizontal start and end visual sections
1351 int left = horizontalHeader->visualIndexAt(dirtyArea.left());
1352 int right = horizontalHeader->visualIndexAt(dirtyArea.right());
1355 if (left == -1) left = 0;
1356 if (right == -1) right = horizontalHeader->count() - 1;
1358 // get the vertical start and end visual sections and if alternate color
1359 int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
1360 if (bottom == -1) bottom = verticalHeader->count() - 1;
1362 bool alternateBase = false;
1363 if (alternate && verticalHeader->sectionsHidden()) {
1364 uint verticalOffset = verticalHeader->offset();
1365 int row = verticalHeader->logicalIndex(top);
1367 ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);
1369 row = verticalHeader->logicalIndex(top);
1370 if (alternate && !verticalHeader->isSectionHidden(row))
1371 alternateBase = !alternateBase;
1374 top = verticalHeader->visualIndexAt(dirtyArea.top());
1375 alternateBase = (top & 1) && alternate;
1377 if (top == -1 || top > bottom)
1380 // Paint each row item
1381 for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
1382 int row = verticalHeader->logicalIndex(visualRowIndex);
1383 if (verticalHeader->isSectionHidden(row))
1385 int rowY = rowViewportPosition(row);
1387 int rowh = rowHeight(row) - gridSize;
1389 // Paint each column item
1390 for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
1391 int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
1392 + visualColumnIndex - firstVisualColumn;
1394 if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
1396 drawn.setBit(currentBit);
1398 int col = horizontalHeader->logicalIndex(visualColumnIndex);
1399 if (horizontalHeader->isSectionHidden(col))
1401 int colp = columnViewportPosition(col);
1403 int colw = columnWidth(col) - gridSize;
1405 const QModelIndex index = d->model->index(row, col, d->root);
1406 if (index.isValid()) {
1407 option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
1410 option.features |= QStyleOptionViewItemV2::Alternate;
1412 option.features &= ~QStyleOptionViewItemV2::Alternate;
1414 d->drawCell(&painter, option, index);
1417 alternateBase = !alternateBase && alternate;
1421 // Find the bottom right (the last rows/columns might be hidden)
1422 while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;
1423 QPen old = painter.pen();
1424 painter.setPen(gridPen);
1426 for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
1427 int row = verticalHeader->logicalIndex(visualIndex);
1428 if (verticalHeader->isSectionHidden(row))
1430 int rowY = rowViewportPosition(row);
1432 int rowh = rowHeight(row) - gridSize;
1433 painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
1436 // Paint each column
1437 for (int h = left; h <= right; ++h) {
1438 int col = horizontalHeader->logicalIndex(h);
1439 if (horizontalHeader->isSectionHidden(col))
1441 int colp = columnViewportPosition(col);
1444 colp += columnWidth(col) - gridSize;
1445 painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
1448 //draw the top & left grid lines if the headers are not visible.
1449 //We do update this line when subsequent scroll happen (see scrollContentsBy)
1450 if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem)
1451 painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0);
1452 if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem)
1453 painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom());
1454 painter.setPen(old);
1458 #ifndef QT_NO_DRAGANDDROP
1459 // Paint the dropIndicator
1460 d->paintDropIndicator(&painter);
1465 Returns the index position of the model item corresponding to the
1466 table item at position \a pos in contents coordinates.
1468 QModelIndex QTableView::indexAt(const QPoint &pos) const
1470 Q_D(const QTableView);
1471 d->executePostedLayout();
1472 int r = rowAt(pos.y());
1473 int c = columnAt(pos.x());
1474 if (r >= 0 && c >= 0) {
1475 if (d->hasSpans()) {
1476 QSpanCollection::Span span = d->span(r, c);
1480 return d->model->index(r, c, d->root);
1482 return QModelIndex();
1486 Returns the horizontal offset of the items in the table view.
1488 Note that the table view uses the horizontal header section
1489 positions to determine the positions of columns in the view.
1491 \sa verticalOffset()
1493 int QTableView::horizontalOffset() const
1495 Q_D(const QTableView);
1496 return d->horizontalHeader->offset();
1500 Returns the vertical offset of the items in the table view.
1502 Note that the table view uses the vertical header section
1503 positions to determine the positions of rows in the view.
1505 \sa horizontalOffset()
1507 int QTableView::verticalOffset() const
1509 Q_D(const QTableView);
1510 return d->verticalHeader->offset();
1514 \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1516 Moves the cursor in accordance with the given \a cursorAction, using the
1517 information provided by the \a modifiers.
1519 \sa QAbstractItemView::CursorAction
1521 QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1524 Q_UNUSED(modifiers);
1526 int bottom = d->model->rowCount(d->root) - 1;
1527 // make sure that bottom is the bottommost *visible* row
1528 while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
1531 int right = d->model->columnCount(d->root) - 1;
1533 while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
1536 if (bottom == -1 || right == -1)
1537 return QModelIndex(); // model is empty
1539 QModelIndex current = currentIndex();
1541 if (!current.isValid()) {
1544 while (column < right && isColumnHidden(d->logicalColumn(column)))
1546 while (isRowHidden(d->logicalRow(row)) && row < bottom)
1548 d->visualCursor = QPoint(column, row);
1549 return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
1552 // Update visual cursor if current index has changed.
1553 QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row()));
1554 if (visualCurrent != d->visualCursor) {
1555 if (d->hasSpans()) {
1556 QSpanCollection::Span span = d->span(current.row(), current.column());
1557 if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom()
1558 || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right())
1559 d->visualCursor = visualCurrent;
1561 d->visualCursor = visualCurrent;
1565 int visualRow = d->visualCursor.y();
1566 if (visualRow > bottom)
1568 Q_ASSERT(visualRow != -1);
1569 int visualColumn = d->visualCursor.x();
1570 if (visualColumn > right)
1571 visualColumn = right;
1572 Q_ASSERT(visualColumn != -1);
1574 if (isRightToLeft()) {
1575 if (cursorAction == MoveLeft)
1576 cursorAction = MoveRight;
1577 else if (cursorAction == MoveRight)
1578 cursorAction = MoveLeft;
1581 switch (cursorAction) {
1583 int originalRow = visualRow;
1584 #ifdef QT_KEYPAD_NAVIGATION
1585 if (QApplication::keypadNavigationEnabled() && visualRow == 0)
1586 visualRow = d->visualRow(model()->rowCount() - 1) + 1;
1587 // FIXME? visualRow = bottom + 1;
1589 int r = d->logicalRow(visualRow);
1590 int c = d->logicalColumn(visualColumn);
1591 if (r != -1 && d->hasSpans()) {
1592 QSpanCollection::Span span = d->span(r, c);
1593 if (span.width() > 1 || span.height() > 1)
1594 visualRow = d->visualRow(span.top());
1596 while (visualRow >= 0) {
1598 r = d->logicalRow(visualRow);
1599 c = d->logicalColumn(visualColumn);
1600 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1604 visualRow = originalRow;
1608 int originalRow = visualRow;
1609 if (d->hasSpans()) {
1610 QSpanCollection::Span span = d->span(current.row(), current.column());
1611 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1613 #ifdef QT_KEYPAD_NAVIGATION
1614 if (QApplication::keypadNavigationEnabled() && visualRow >= bottom)
1617 int r = d->logicalRow(visualRow);
1618 int c = d->logicalColumn(visualColumn);
1619 if (r != -1 && d->hasSpans()) {
1620 QSpanCollection::Span span = d->span(r, c);
1621 if (span.width() > 1 || span.height() > 1)
1622 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1624 while (visualRow <= bottom) {
1626 r = d->logicalRow(visualRow);
1627 c = d->logicalColumn(visualColumn);
1628 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1631 if (visualRow > bottom)
1632 visualRow = originalRow;
1637 int originalRow = visualRow;
1638 int originalColumn = visualColumn;
1639 bool firstTime = true;
1640 bool looped = false;
1641 bool wrapped = false;
1643 int r = d->logicalRow(visualRow);
1644 int c = d->logicalColumn(visualColumn);
1645 if (firstTime && c != -1 && d->hasSpans()) {
1647 QSpanCollection::Span span = d->span(r, c);
1648 if (span.width() > 1 || span.height() > 1)
1649 visualColumn = d->visualColumn(span.left());
1651 while (visualColumn >= 0) {
1653 r = d->logicalRow(visualRow);
1654 c = d->logicalColumn(visualColumn);
1655 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1657 if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) {
1662 if (cursorAction == MoveLeft || visualColumn >= 0)
1664 visualColumn = right + 1;
1665 if (visualRow == 0) {
1672 if (visualColumn < 0)
1673 visualColumn = originalColumn;
1678 int originalRow = visualRow;
1679 int originalColumn = visualColumn;
1680 bool firstTime = true;
1681 bool looped = false;
1682 bool wrapped = false;
1684 int r = d->logicalRow(visualRow);
1685 int c = d->logicalColumn(visualColumn);
1686 if (firstTime && c != -1 && d->hasSpans()) {
1688 QSpanCollection::Span span = d->span(r, c);
1689 if (span.width() > 1 || span.height() > 1)
1690 visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1692 while (visualColumn <= right) {
1694 r = d->logicalRow(visualRow);
1695 c = d->logicalColumn(visualColumn);
1696 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1698 if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) {
1703 if (cursorAction == MoveRight || visualColumn <= right)
1706 if (visualRow == bottom) {
1713 if (visualColumn > right)
1714 visualColumn = originalColumn;
1719 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1721 if (modifiers & Qt::ControlModifier) {
1723 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1728 visualColumn = right;
1729 if (modifiers & Qt::ControlModifier)
1733 int newRow = rowAt(visualRect(current).top() - d->viewport->height());
1735 newRow = d->logicalRow(0);
1736 return d->model->index(newRow, current.column(), d->root);
1738 case MovePageDown: {
1739 int newRow = rowAt(visualRect(current).bottom() + d->viewport->height());
1741 newRow = d->logicalRow(bottom);
1742 return d->model->index(newRow, current.column(), d->root);
1745 d->visualCursor = QPoint(visualColumn, visualRow);
1746 int logicalRow = d->logicalRow(visualRow);
1747 int logicalColumn = d->logicalColumn(visualColumn);
1748 if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
1749 return QModelIndex();
1751 QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
1752 if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result))
1755 return QModelIndex();
1759 \fn void QTableView::setSelection(const QRect &rect,
1760 QItemSelectionModel::SelectionFlags flags)
1762 Selects the items within the given \a rect and in accordance with
1763 the specified selection \a flags.
1765 void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1768 QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
1769 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
1770 QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
1771 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
1772 if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
1775 bool verticalMoved = verticalHeader()->sectionsMoved();
1776 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1778 QItemSelection selection;
1780 if (d->hasSpans()) {
1782 int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
1783 int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1784 int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
1785 int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1788 foreach (QSpanCollection::Span *it, d->spans.spans) {
1789 const QSpanCollection::Span &span = *it;
1790 int t = d->visualRow(span.top());
1791 int l = d->visualColumn(span.left());
1792 int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1793 int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1794 if ((t > bottom) || (l > right) || (top > b) || (left > r))
1795 continue; // no intersect
1816 for (int horizontal = left; horizontal <= right; ++horizontal) {
1817 int column = d->logicalColumn(horizontal);
1818 for (int vertical = top; vertical <= bottom; ++vertical) {
1819 int row = d->logicalRow(vertical);
1820 QModelIndex index = d->model->index(row, column, d->root);
1821 selection.append(QItemSelectionRange(index));
1824 } else if (verticalMoved && horizontalMoved) {
1825 int top = d->visualRow(tl.row());
1826 int left = d->visualColumn(tl.column());
1827 int bottom = d->visualRow(br.row());
1828 int right = d->visualColumn(br.column());
1829 for (int horizontal = left; horizontal <= right; ++horizontal) {
1830 int column = d->logicalColumn(horizontal);
1831 for (int vertical = top; vertical <= bottom; ++vertical) {
1832 int row = d->logicalRow(vertical);
1833 QModelIndex index = d->model->index(row, column, d->root);
1834 selection.append(QItemSelectionRange(index));
1837 } else if (horizontalMoved) {
1838 int left = d->visualColumn(tl.column());
1839 int right = d->visualColumn(br.column());
1840 for (int visual = left; visual <= right; ++visual) {
1841 int column = d->logicalColumn(visual);
1842 QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
1843 QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
1844 selection.append(QItemSelectionRange(topLeft, bottomRight));
1846 } else if (verticalMoved) {
1847 int top = d->visualRow(tl.row());
1848 int bottom = d->visualRow(br.row());
1849 for (int visual = top; visual <= bottom; ++visual) {
1850 int row = d->logicalRow(visual);
1851 QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
1852 QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
1853 selection.append(QItemSelectionRange(topLeft, bottomRight));
1855 } else { // nothing moved
1856 QItemSelectionRange range(tl, br);
1857 if (!range.isEmpty())
1858 selection.append(range);
1861 d->selectionModel->select(selection, command);
1867 Returns the rectangle from the viewport of the items in the given
1870 Since 4.7, the returned region only contains rectangles intersecting
1871 (or included in) the viewport.
1873 QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const
1875 Q_D(const QTableView);
1877 if (selection.isEmpty())
1880 QRegion selectionRegion;
1881 const QRect &viewportRect = d->viewport->rect();
1882 bool verticalMoved = verticalHeader()->sectionsMoved();
1883 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1885 if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) {
1886 for (int i = 0; i < selection.count(); ++i) {
1887 QItemSelectionRange range = selection.at(i);
1888 if (range.parent() != d->root || !range.isValid())
1890 for (int r = range.top(); r <= range.bottom(); ++r)
1891 for (int c = range.left(); c <= range.right(); ++c) {
1892 const QRect &rangeRect = visualRect(d->model->index(r, c, d->root));
1893 if (viewportRect.intersects(rangeRect))
1894 selectionRegion += rangeRect;
1897 } else if (horizontalMoved) {
1898 for (int i = 0; i < selection.count(); ++i) {
1899 QItemSelectionRange range = selection.at(i);
1900 if (range.parent() != d->root || !range.isValid())
1902 int top = rowViewportPosition(range.top());
1903 int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1905 qSwap<int>(top, bottom);
1906 int height = bottom - top;
1907 for (int c = range.left(); c <= range.right(); ++c) {
1908 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
1909 if (viewportRect.intersects(rangeRect))
1910 selectionRegion += rangeRect;
1913 } else if (verticalMoved) {
1914 for (int i = 0; i < selection.count(); ++i) {
1915 QItemSelectionRange range = selection.at(i);
1916 if (range.parent() != d->root || !range.isValid())
1918 int left = columnViewportPosition(range.left());
1919 int right = columnViewportPosition(range.right()) + columnWidth(range.right());
1921 qSwap<int>(left, right);
1922 int width = right - left;
1923 for (int r = range.top(); r <= range.bottom(); ++r) {
1924 const QRect rangeRect(left, rowViewportPosition(r), width, rowHeight(r));
1925 if (viewportRect.intersects(rangeRect))
1926 selectionRegion += rangeRect;
1929 } else { // nothing moved
1930 const int gridAdjust = showGrid() ? 1 : 0;
1931 for (int i = 0; i < selection.count(); ++i) {
1932 QItemSelectionRange range = selection.at(i);
1933 if (range.parent() != d->root || !range.isValid())
1935 d->trimHiddenSelections(&range);
1937 const int rtop = rowViewportPosition(range.top());
1938 const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1941 if (isLeftToRight()) {
1942 rleft = columnViewportPosition(range.left());
1943 rright = columnViewportPosition(range.right()) + columnWidth(range.right());
1945 rleft = columnViewportPosition(range.right());
1946 rright = columnViewportPosition(range.left()) + columnWidth(range.left());
1948 const QRect rangeRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust));
1949 if (viewportRect.intersects(rangeRect))
1950 selectionRegion += rangeRect;
1951 if (d->hasSpans()) {
1952 foreach (QSpanCollection::Span *s,
1953 d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) {
1954 if (range.contains(s->top(), s->left(), range.parent())) {
1955 const QRect &visualSpanRect = d->visualSpanRect(*s);
1956 if (viewportRect.intersects(visualSpanRect))
1957 selectionRegion += visualSpanRect;
1964 return selectionRegion;
1971 QModelIndexList QTableView::selectedIndexes() const
1973 Q_D(const QTableView);
1974 QModelIndexList viewSelected;
1975 QModelIndexList modelSelected;
1976 if (d->selectionModel)
1977 modelSelected = d->selectionModel->selectedIndexes();
1978 for (int i = 0; i < modelSelected.count(); ++i) {
1979 QModelIndex index = modelSelected.at(i);
1980 if (!isIndexHidden(index) && index.parent() == d->root)
1981 viewSelected.append(index);
1983 return viewSelected;
1988 This slot is called whenever rows are added or deleted. The
1989 previous number of rows is specified by \a oldCount, and the new
1990 number of rows is specified by \a newCount.
1992 void QTableView::rowCountChanged(int oldCount, int newCount )
1995 //when removing rows, we need to disable updates for the header until the geometries have been
1996 //updated and the offset has been adjusted, or we risk calling paintSection for all the sections
1997 if (newCount < oldCount)
1998 d->verticalHeader->setUpdatesEnabled(false);
1999 d->doDelayedItemsLayout();
2003 This slot is called whenever columns are added or deleted. The
2004 previous number of columns is specified by \a oldCount, and the new
2005 number of columns is specified by \a newCount.
2007 void QTableView::columnCountChanged(int, int)
2011 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
2012 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
2014 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
2015 d->viewport->update();
2021 void QTableView::updateGeometries()
2024 if (d->geometryRecursionBlock)
2026 d->geometryRecursionBlock = true;
2029 if (!d->verticalHeader->isHidden()) {
2030 width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
2031 width = qMin(width, d->verticalHeader->maximumWidth());
2034 if (!d->horizontalHeader->isHidden()) {
2035 height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
2036 height = qMin(height, d->horizontalHeader->maximumHeight());
2038 bool reverse = isRightToLeft();
2040 setViewportMargins(0, height, width, 0);
2042 setViewportMargins(width, height, 0, 0);
2046 QRect vg = d->viewport->geometry();
2048 int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
2049 d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
2050 if (d->verticalHeader->isHidden())
2051 QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
2053 int horizontalTop = vg.top() - height;
2054 d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
2055 if (d->horizontalHeader->isHidden())
2056 QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
2058 // update cornerWidget
2059 if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
2060 d->cornerWidget->setHidden(true);
2062 d->cornerWidget->setHidden(false);
2063 d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
2066 // update scroll bars
2068 // ### move this block into the if
2069 QSize vsize = d->viewport->size();
2070 QSize max = maximumViewportSize();
2071 uint horizontalLength = d->horizontalHeader->length();
2072 uint verticalLength = d->verticalHeader->length();
2073 if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength)
2076 // horizontal scroll bar
2077 const int columnCount = d->horizontalHeader->count();
2078 const int viewportWidth = vsize.width();
2079 int columnsInViewport = 0;
2080 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
2081 int logical = d->horizontalHeader->logicalIndex(column);
2082 if (!d->horizontalHeader->isSectionHidden(logical)) {
2083 width += d->horizontalHeader->sectionSize(logical);
2084 if (width > viewportWidth)
2086 ++columnsInViewport;
2089 columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column
2091 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2092 const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
2093 horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
2094 horizontalScrollBar()->setPageStep(columnsInViewport);
2095 if (columnsInViewport >= visibleColumns)
2096 d->horizontalHeader->setOffset(0);
2097 horizontalScrollBar()->setSingleStep(1);
2098 } else { // ScrollPerPixel
2099 horizontalScrollBar()->setPageStep(vsize.width());
2100 horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
2101 horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
2104 // vertical scroll bar
2105 const int rowCount = d->verticalHeader->count();
2106 const int viewportHeight = vsize.height();
2107 int rowsInViewport = 0;
2108 for (int height = 0, row = rowCount - 1; row >= 0; --row) {
2109 int logical = d->verticalHeader->logicalIndex(row);
2110 if (!d->verticalHeader->isSectionHidden(logical)) {
2111 height += d->verticalHeader->sectionSize(logical);
2112 if (height > viewportHeight)
2117 rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row
2119 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2120 const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
2121 verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
2122 verticalScrollBar()->setPageStep(rowsInViewport);
2123 if (rowsInViewport >= visibleRows)
2124 d->verticalHeader->setOffset(0);
2125 verticalScrollBar()->setSingleStep(1);
2126 } else { // ScrollPerPixel
2127 verticalScrollBar()->setPageStep(vsize.height());
2128 verticalScrollBar()->setRange(0, verticalLength - vsize.height());
2129 verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
2132 d->geometryRecursionBlock = false;
2133 QAbstractItemView::updateGeometries();
2137 Returns the size hint for the given \a row's height or -1 if there
2140 If you need to set the height of a given row to a fixed value, call
2141 QHeaderView::resizeSection() on the table's vertical header.
2143 If you reimplement this function in a subclass, note that the value you
2144 return is only used when resizeRowToContents() is called. In that case,
2145 if a larger row height is required by either the vertical header or
2146 the item delegate, that width will be used instead.
2148 \sa QWidget::sizeHint, verticalHeader()
2150 int QTableView::sizeHintForRow(int row) const
2152 Q_D(const QTableView);
2159 int left = qMax(0, d->horizontalHeader->visualIndexAt(0));
2160 int right = d->horizontalHeader->visualIndexAt(d->viewport->width());
2161 if (right == -1) // the table don't have enough columns to fill the viewport
2162 right = d->model->columnCount(d->root) - 1;
2164 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2168 for (int column = left; column <= right; ++column) {
2169 int logicalColumn = d->horizontalHeader->logicalIndex(column);
2170 if (d->horizontalHeader->isSectionHidden(logicalColumn))
2172 index = d->model->index(row, logicalColumn, d->root);
2173 if (d->wrapItemText) {// for wrapping boundaries
2174 option.rect.setY(rowViewportPosition(index.row()));
2175 option.rect.setHeight(rowHeight(index.row()));
2176 option.rect.setX(columnViewportPosition(index.column()));
2177 option.rect.setWidth(columnWidth(index.column()));
2180 QWidget *editor = d->editorForIndex(index).widget.data();
2181 if (editor && d->persistent.contains(editor)) {
2182 hint = qMax(hint, editor->sizeHint().height());
2183 int min = editor->minimumSize().height();
2184 int max = editor->maximumSize().height();
2185 hint = qBound(min, hint, max);
2188 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height());
2191 return d->showGrid ? hint + 1 : hint;
2195 Returns the size hint for the given \a column's width or -1 if
2198 If you need to set the width of a given column to a fixed value, call
2199 QHeaderView::resizeSection() on the table's horizontal header.
2201 If you reimplement this function in a subclass, note that the value you
2202 return will be used when resizeColumnToContents() or
2203 QHeaderView::resizeSections() is called. If a larger column width is
2204 required by either the horizontal header or the item delegate, the larger
2205 width will be used instead.
2207 \sa QWidget::sizeHint, horizontalHeader()
2209 int QTableView::sizeHintForColumn(int column) const
2211 Q_D(const QTableView);
2218 int top = qMax(0, d->verticalHeader->visualIndexAt(0));
2219 int bottom = d->verticalHeader->visualIndexAt(d->viewport->height());
2220 if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
2221 bottom = d->model->rowCount(d->root) - 1;
2223 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2227 for (int row = top; row <= bottom; ++row) {
2228 int logicalRow = d->verticalHeader->logicalIndex(row);
2229 if (d->verticalHeader->isSectionHidden(logicalRow))
2231 index = d->model->index(logicalRow, column, d->root);
2233 QWidget *editor = d->editorForIndex(index).widget.data();
2234 if (editor && d->persistent.contains(editor)) {
2235 hint = qMax(hint, editor->sizeHint().width());
2236 int min = editor->minimumSize().width();
2237 int max = editor->maximumSize().width();
2238 hint = qBound(min, hint, max);
2241 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width());
2244 return d->showGrid ? hint + 1 : hint;
2248 Returns the y-coordinate in contents coordinates of the given \a
2251 int QTableView::rowViewportPosition(int row) const
2253 Q_D(const QTableView);
2254 return d->verticalHeader->sectionViewportPosition(row);
2258 Returns the row in which the given y-coordinate, \a y, in contents
2259 coordinates is located.
2261 \note This function returns -1 if the given coordinate is not valid
2266 int QTableView::rowAt(int y) const
2268 Q_D(const QTableView);
2269 return d->verticalHeader->logicalIndexAt(y);
2275 Sets the height of the given \a row to be \a height.
2277 void QTableView::setRowHeight(int row, int height)
2279 Q_D(const QTableView);
2280 d->verticalHeader->resizeSection(row, height);
2284 Returns the height of the given \a row.
2286 \sa resizeRowToContents(), columnWidth()
2288 int QTableView::rowHeight(int row) const
2290 Q_D(const QTableView);
2291 return d->verticalHeader->sectionSize(row);
2295 Returns the x-coordinate in contents coordinates of the given \a
2298 int QTableView::columnViewportPosition(int column) const
2300 Q_D(const QTableView);
2301 return d->horizontalHeader->sectionViewportPosition(column);
2305 Returns the column in which the given x-coordinate, \a x, in contents
2306 coordinates is located.
2308 \note This function returns -1 if the given coordinate is not valid
2313 int QTableView::columnAt(int x) const
2315 Q_D(const QTableView);
2316 return d->horizontalHeader->logicalIndexAt(x);
2322 Sets the width of the given \a column to be \a width.
2324 void QTableView::setColumnWidth(int column, int width)
2326 Q_D(const QTableView);
2327 d->horizontalHeader->resizeSection(column, width);
2331 Returns the width of the given \a column.
2333 \sa resizeColumnToContents(), rowHeight()
2335 int QTableView::columnWidth(int column) const
2337 Q_D(const QTableView);
2338 return d->horizontalHeader->sectionSize(column);
2342 Returns true if the given \a row is hidden; otherwise returns false.
2344 \sa isColumnHidden()
2346 bool QTableView::isRowHidden(int row) const
2348 Q_D(const QTableView);
2349 return d->verticalHeader->isSectionHidden(row);
2353 If \a hide is true \a row will be hidden, otherwise it will be shown.
2355 \sa setColumnHidden()
2357 void QTableView::setRowHidden(int row, bool hide)
2360 if (row < 0 || row >= d->verticalHeader->count())
2362 d->verticalHeader->setSectionHidden(row, hide);
2366 Returns true if the given \a column is hidden; otherwise returns false.
2370 bool QTableView::isColumnHidden(int column) const
2372 Q_D(const QTableView);
2373 return d->horizontalHeader->isSectionHidden(column);
2377 If \a hide is true the given \a column will be hidden; otherwise it
2382 void QTableView::setColumnHidden(int column, bool hide)
2385 if (column < 0 || column >= d->horizontalHeader->count())
2387 d->horizontalHeader->setSectionHidden(column, hide);
2392 \property QTableView::sortingEnabled
2393 \brief whether sorting is enabled
2395 If this property is true, sorting is enabled for the table. If
2396 this property is false, sorting is not enabled. The default value
2399 \note. Setting the property to true with setSortingEnabled()
2400 immediately triggers a call to sortByColumn() with the current
2401 sort section and order.
2407 If \a enabled true enables sorting for the table and immediately
2408 trigger a call to sortByColumn() with the current sort section and
2411 void QTableView::setSortingEnabled(bool enable)
2414 d->sortingEnabled = enable;
2415 horizontalHeader()->setSortIndicatorShown(enable);
2417 disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2418 this, SLOT(_q_selectColumn(int)));
2419 disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2420 this, SLOT(selectColumn(int)));
2421 connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2422 this, SLOT(sortByColumn(int)), Qt::UniqueConnection);
2423 sortByColumn(horizontalHeader()->sortIndicatorSection(),
2424 horizontalHeader()->sortIndicatorOrder());
2426 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2427 this, SLOT(_q_selectColumn(int)), Qt::UniqueConnection);
2428 connect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2429 this, SLOT(selectColumn(int)), Qt::UniqueConnection);
2430 disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2431 this, SLOT(sortByColumn(int)));
2435 bool QTableView::isSortingEnabled() const
2437 Q_D(const QTableView);
2438 return d->sortingEnabled;
2442 \property QTableView::showGrid
2443 \brief whether the grid is shown
2445 If this property is true a grid is drawn for the table; if the
2446 property is false, no grid is drawn. The default value is true.
2448 bool QTableView::showGrid() const
2450 Q_D(const QTableView);
2454 void QTableView::setShowGrid(bool show)
2457 if (d->showGrid != show) {
2459 d->viewport->update();
2464 \property QTableView::gridStyle
2465 \brief the pen style used to draw the grid.
2467 This property holds the style used when drawing the grid (see \l{showGrid}).
2469 Qt::PenStyle QTableView::gridStyle() const
2471 Q_D(const QTableView);
2472 return d->gridStyle;
2475 void QTableView::setGridStyle(Qt::PenStyle style)
2478 if (d->gridStyle != style) {
2479 d->gridStyle = style;
2480 d->viewport->update();
2485 \property QTableView::wordWrap
2486 \brief the item text word-wrapping policy
2489 If this property is true then the item text is wrapped where
2490 necessary at word-breaks; otherwise it is not wrapped at all.
2491 This property is true by default.
2493 Note that even of wrapping is enabled, the cell will not be
2494 expanded to fit all text. Ellipsis will be inserted according to
2495 the current \l{QAbstractItemView::}{textElideMode}.
2498 void QTableView::setWordWrap(bool on)
2501 if (d->wrapItemText == on)
2503 d->wrapItemText = on;
2504 QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
2505 QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
2508 bool QTableView::wordWrap() const
2510 Q_D(const QTableView);
2511 return d->wrapItemText;
2515 \property QTableView::cornerButtonEnabled
2516 \brief whether the button in the top-left corner is enabled
2519 If this property is true then button in the top-left corner
2520 of the table view is enabled. Clicking on this button will
2521 select all the cells in the table view.
2523 This property is true by default.
2525 void QTableView::setCornerButtonEnabled(bool enable)
2528 d->cornerWidget->setEnabled(enable);
2531 bool QTableView::isCornerButtonEnabled() const
2533 Q_D(const QTableView);
2534 return d->cornerWidget->isEnabled();
2540 Returns the rectangle on the viewport occupied by the given \a
2542 If the index is hidden in the view it will return a null QRect.
2544 QRect QTableView::visualRect(const QModelIndex &index) const
2546 Q_D(const QTableView);
2547 if (!d->isIndexValid(index) || index.parent() != d->root
2548 || (!d->hasSpans() && isIndexHidden(index)))
2551 d->executePostedLayout();
2553 if (d->hasSpans()) {
2554 QSpanCollection::Span span = d->span(index.row(), index.column());
2555 return d->visualSpanRect(span);
2558 int rowp = rowViewportPosition(index.row());
2559 int rowh = rowHeight(index.row());
2560 int colp = columnViewportPosition(index.column());
2561 int colw = columnWidth(index.column());
2563 const int i = showGrid() ? 1 : 0;
2564 return QRect(colp, rowp, colw - i, rowh - i);
2570 Makes sure that the given \a item is visible in the table view,
2571 scrolling if necessary.
2573 void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
2577 // check if we really need to do anything
2578 if (!d->isIndexValid(index)
2579 || (d->model->parent(index) != d->root)
2580 || isRowHidden(index.row()) || isColumnHidden(index.column()))
2583 QSpanCollection::Span span;
2585 span = d->span(index.row(), index.column());
2587 // Adjust horizontal position
2589 int viewportWidth = d->viewport->width();
2590 int horizontalOffset = d->horizontalHeader->offset();
2591 int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
2592 int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
2593 int cellWidth = d->hasSpans()
2594 ? d->columnSpanWidth(index.column(), span.width())
2595 : d->horizontalHeader->sectionSize(index.column());
2597 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2599 bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
2600 bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
2602 if (hint == PositionAtCenter || positionAtRight) {
2603 int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
2605 while (horizontalIndex > 0) {
2606 x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
2613 if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
2614 int hiddenSections = 0;
2615 if (d->horizontalHeader->sectionsHidden()) {
2616 for (int s = horizontalIndex - 1; s >= 0; --s) {
2617 int column = d->horizontalHeader->logicalIndex(s);
2618 if (d->horizontalHeader->isSectionHidden(column))
2622 horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
2625 } else { // ScrollPerPixel
2626 if (hint == PositionAtCenter) {
2627 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
2629 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
2630 horizontalScrollBar()->setValue(horizontalPosition);
2631 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
2632 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
2636 // Adjust vertical position
2638 int viewportHeight = d->viewport->height();
2639 int verticalOffset = d->verticalHeader->offset();
2640 int verticalPosition = d->verticalHeader->sectionPosition(index.row());
2641 int verticalIndex = d->verticalHeader->visualIndex(index.row());
2642 int cellHeight = d->hasSpans()
2643 ? d->rowSpanHeight(index.row(), span.height())
2644 : d->verticalHeader->sectionSize(index.row());
2646 if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
2647 if (hint == EnsureVisible)
2648 hint = PositionAtTop;
2649 } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
2650 if (hint == EnsureVisible)
2651 hint = PositionAtBottom;
2654 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2656 if (hint == PositionAtBottom || hint == PositionAtCenter) {
2657 int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
2659 while (verticalIndex > 0) {
2660 int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
2661 y += d->verticalHeader->sectionSize(row);
2668 if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) {
2669 int hiddenSections = 0;
2670 if (d->verticalHeader->sectionsHidden()) {
2671 for (int s = verticalIndex - 1; s >= 0; --s) {
2672 int row = d->verticalHeader->logicalIndex(s);
2673 if (d->verticalHeader->isSectionHidden(row))
2677 verticalScrollBar()->setValue(verticalIndex - hiddenSections);
2680 } else { // ScrollPerPixel
2681 if (hint == PositionAtTop) {
2682 verticalScrollBar()->setValue(verticalPosition);
2683 } else if (hint == PositionAtBottom) {
2684 verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
2685 } else if (hint == PositionAtCenter) {
2686 verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
2694 This slot is called to change the height of the given \a row. The
2695 old height is specified by \a oldHeight, and the new height by \a
2700 void QTableView::rowResized(int row, int, int)
2703 d->rowsToUpdate.append(row);
2704 if (d->rowResizeTimerID == 0)
2705 d->rowResizeTimerID = startTimer(0);
2709 This slot is called to change the width of the given \a column.
2710 The old width is specified by \a oldWidth, and the new width by \a
2715 void QTableView::columnResized(int column, int, int)
2718 d->columnsToUpdate.append(column);
2719 if (d->columnResizeTimerID == 0)
2720 d->columnResizeTimerID = startTimer(0);
2726 void QTableView::timerEvent(QTimerEvent *event)
2730 if (event->timerId() == d->columnResizeTimerID) {
2732 killTimer(d->columnResizeTimerID);
2733 d->columnResizeTimerID = 0;
2736 int viewportHeight = d->viewport->height();
2737 int viewportWidth = d->viewport->width();
2738 if (d->hasSpans()) {
2739 rect = QRect(0, 0, viewportWidth, viewportHeight);
2741 for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
2742 int column = d->columnsToUpdate.at(i);
2743 int x = columnViewportPosition(column);
2744 if (isRightToLeft())
2745 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
2747 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
2751 d->viewport->update(rect.normalized());
2752 d->columnsToUpdate.clear();
2755 if (event->timerId() == d->rowResizeTimerID) {
2757 killTimer(d->rowResizeTimerID);
2758 d->rowResizeTimerID = 0;
2760 int viewportHeight = d->viewport->height();
2761 int viewportWidth = d->viewport->width();
2763 if (d->hasSpans()) {
2766 top = viewportHeight;
2767 for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
2768 int y = rowViewportPosition(d->rowsToUpdate.at(i));
2773 d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
2774 d->rowsToUpdate.clear();
2777 QAbstractItemView::timerEvent(event);
2781 This slot is called to change the index of the given \a row in the
2782 table view. The old index is specified by \a oldIndex, and the new
2783 index by \a newIndex.
2787 void QTableView::rowMoved(int, int oldIndex, int newIndex)
2792 int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
2793 int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
2794 if (d->hasSpans()) {
2795 d->viewport->update();
2797 int oldTop = rowViewportPosition(logicalOldIndex);
2798 int newTop = rowViewportPosition(logicalNewIndex);
2799 int oldBottom = oldTop + rowHeight(logicalOldIndex);
2800 int newBottom = newTop + rowHeight(logicalNewIndex);
2801 int top = qMin(oldTop, newTop);
2802 int bottom = qMax(oldBottom, newBottom);
2803 int height = bottom - top;
2804 d->viewport->update(0, top, d->viewport->width(), height);
2809 This slot is called to change the index of the given \a column in
2810 the table view. The old index is specified by \a oldIndex, and
2811 the new index by \a newIndex.
2815 void QTableView::columnMoved(int, int oldIndex, int newIndex)
2820 int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
2821 int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
2822 if (d->hasSpans()) {
2823 d->viewport->update();
2825 int oldLeft = columnViewportPosition(logicalOldIndex);
2826 int newLeft = columnViewportPosition(logicalNewIndex);
2827 int oldRight = oldLeft + columnWidth(logicalOldIndex);
2828 int newRight = newLeft + columnWidth(logicalNewIndex);
2829 int left = qMin(oldLeft, newLeft);
2830 int right = qMax(oldRight, newRight);
2831 int width = right - left;
2832 d->viewport->update(left, 0, width, d->viewport->height());
2837 Selects the given \a row in the table view if the current
2838 SelectionMode and SelectionBehavior allows rows to be selected.
2842 void QTableView::selectRow(int row)
2845 d->selectRow(row, true);
2849 Selects the given \a column in the table view if the current
2850 SelectionMode and SelectionBehavior allows columns to be selected.
2854 void QTableView::selectColumn(int column)
2857 d->selectColumn(column, true);
2861 Hide the given \a row.
2863 \sa showRow() hideColumn()
2865 void QTableView::hideRow(int row)
2868 d->verticalHeader->hideSection(row);
2872 Hide the given \a column.
2874 \sa showColumn() hideRow()
2876 void QTableView::hideColumn(int column)
2879 d->horizontalHeader->hideSection(column);
2883 Show the given \a row.
2885 \sa hideRow() showColumn()
2887 void QTableView::showRow(int row)
2890 d->verticalHeader->showSection(row);
2894 Show the given \a column.
2896 \sa hideColumn() showRow()
2898 void QTableView::showColumn(int column)
2901 d->horizontalHeader->showSection(column);
2905 Resizes the given \a row based on the size hints of the delegate
2906 used to render each item in the row.
2908 void QTableView::resizeRowToContents(int row)
2911 int content = sizeHintForRow(row);
2912 int header = d->verticalHeader->sectionSizeHint(row);
2913 d->verticalHeader->resizeSection(row, qMax(content, header));
2917 Resizes all rows based on the size hints of the delegate
2918 used to render each item in the rows.
2920 void QTableView::resizeRowsToContents()
2923 d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
2927 Resizes the given \a column based on the size hints of the delegate
2928 used to render each item in the column.
2930 \note Only visible columns will be resized. Reimplement sizeHintForColumn()
2931 to resize hidden columns as well.
2933 void QTableView::resizeColumnToContents(int column)
2936 int content = sizeHintForColumn(column);
2937 int header = d->horizontalHeader->sectionSizeHint(column);
2938 d->horizontalHeader->resizeSection(column, qMax(content, header));
2942 Resizes all columns based on the size hints of the delegate
2943 used to render each item in the columns.
2945 void QTableView::resizeColumnsToContents()
2948 d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
2955 Sorts the model by the values in the given \a column.
2957 void QTableView::sortByColumn(int column)
2962 d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
2968 Sorts the model by the values in the given \a column in the given \a order.
2972 void QTableView::sortByColumn(int column, Qt::SortOrder order)
2975 d->horizontalHeader->setSortIndicator(column, order);
2976 sortByColumn(column);
2982 void QTableView::verticalScrollbarAction(int action)
2984 QAbstractItemView::verticalScrollbarAction(action);
2990 void QTableView::horizontalScrollbarAction(int action)
2992 QAbstractItemView::horizontalScrollbarAction(action);
2998 bool QTableView::isIndexHidden(const QModelIndex &index) const
3000 Q_D(const QTableView);
3001 Q_ASSERT(d->isIndexValid(index));
3002 if (isRowHidden(index.row()) || isColumnHidden(index.column()))
3004 if (d->hasSpans()) {
3005 QSpanCollection::Span span = d->span(index.row(), index.column());
3006 return !((span.top() == index.row()) && (span.left() == index.column()));
3012 \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
3015 Sets the span of the table element at (\a row, \a column) to the number of
3016 rows and columns specified by (\a rowSpanCount, \a columnSpanCount).
3018 \sa rowSpan(), columnSpan()
3020 void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
3023 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
3025 d->setSpan(row, column, rowSpan, columnSpan);
3026 d->viewport->update();
3032 Returns the row span of the table element at (\a row, \a column).
3035 \sa setSpan(), columnSpan()
3037 int QTableView::rowSpan(int row, int column) const
3039 Q_D(const QTableView);
3040 return d->rowSpan(row, column);
3046 Returns the column span of the table element at (\a row, \a
3047 column). The default is 1.
3049 \sa setSpan(), rowSpan()
3051 int QTableView::columnSpan(int row, int column) const
3053 Q_D(const QTableView);
3054 return d->columnSpan(row, column);
3060 Removes all row and column spans in the table view.
3065 void QTableView::clearSpans()
3069 d->viewport->update();
3072 void QTableViewPrivate::_q_selectRow(int row)
3074 selectRow(row, false);
3077 void QTableViewPrivate::_q_selectColumn(int column)
3079 selectColumn(column, false);
3082 void QTableViewPrivate::selectRow(int row, bool anchor)
3086 if (q->selectionBehavior() == QTableView::SelectColumns
3087 || (q->selectionMode() == QTableView::SingleSelection
3088 && q->selectionBehavior() == QTableView::SelectItems))
3091 if (row >= 0 && row < model->rowCount(root)) {
3092 int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
3093 QModelIndex index = model->index(row, column, root);
3094 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3095 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3096 if ((anchor && !(command & QItemSelectionModel::Current))
3097 || (q->selectionMode() == QTableView::SingleSelection))
3098 rowSectionAnchor = row;
3100 if (q->selectionMode() != QTableView::SingleSelection
3101 && command.testFlag(QItemSelectionModel::Toggle)) {
3103 ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index)
3104 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3105 command &= ~QItemSelectionModel::Toggle;
3106 command |= ctrlDragSelectionFlag;
3108 command |= QItemSelectionModel::Current;
3111 QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root);
3112 QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root);
3113 if (verticalHeader->sectionsMoved() && tl.row() != br.row())
3114 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3116 selectionModel->select(QItemSelection(tl, br), command);
3120 void QTableViewPrivate::selectColumn(int column, bool anchor)
3124 if (q->selectionBehavior() == QTableView::SelectRows
3125 || (q->selectionMode() == QTableView::SingleSelection
3126 && q->selectionBehavior() == QTableView::SelectItems))
3129 if (column >= 0 && column < model->columnCount(root)) {
3130 int row = verticalHeader->logicalIndexAt(0);
3131 QModelIndex index = model->index(row, column, root);
3132 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3133 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3134 if ((anchor && !(command & QItemSelectionModel::Current))
3135 || (q->selectionMode() == QTableView::SingleSelection))
3136 columnSectionAnchor = column;
3138 if (q->selectionMode() != QTableView::SingleSelection
3139 && command.testFlag(QItemSelectionModel::Toggle)) {
3141 ctrlDragSelectionFlag = horizontalHeader->selectionModel()->selectedColumns().contains(index)
3142 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3143 command &= ~QItemSelectionModel::Toggle;
3144 command |= ctrlDragSelectionFlag;
3146 command |= QItemSelectionModel::Current;
3149 QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root);
3150 QModelIndex br = model->index(model->rowCount(root) - 1,
3151 qMax(columnSectionAnchor, column), root);
3152 if (horizontalHeader->sectionsMoved() && tl.column() != br.column())
3153 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3155 selectionModel->select(QItemSelection(tl, br), command);
3162 void QTableView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
3164 #ifndef QT_NO_ACCESSIBILITY
3165 if (QAccessible::isActive()) {
3166 if (current.isValid()) {
3167 int entry = visualIndex(current) + 1;
3168 if (horizontalHeader())
3170 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
3174 QAbstractItemView::currentChanged(current, previous);
3180 void QTableView::selectionChanged(const QItemSelection &selected,
3181 const QItemSelection &deselected)
3183 #ifndef QT_NO_ACCESSIBILITY
3184 if (QAccessible::isActive()) {
3185 // ### does not work properly for selection ranges.
3186 QModelIndex sel = selected.indexes().value(0);
3187 if (sel.isValid()) {
3188 int entry = visualIndex(sel);
3189 if (horizontalHeader())
3191 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
3193 QModelIndex desel = deselected.indexes().value(0);
3194 if (desel.isValid()) {
3195 int entry = visualIndex(sel);
3196 if (horizontalHeader())
3198 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
3202 QAbstractItemView::selectionChanged(selected, deselected);
3205 int QTableView::visualIndex(const QModelIndex &index) const
3212 #include "qtableview.moc"
3214 #include "moc_qtableview.cpp"
3216 #endif // QT_NO_TABLEVIEW