source: trunk/src/gui/itemviews/qtableview.cpp@ 123

Last change on this file since 123 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 83.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qtableview.h"
43
44#ifndef QT_NO_TABLEVIEW
45#include <qheaderview.h>
46#include <qitemdelegate.h>
47#include <qapplication.h>
48#include <qpainter.h>
49#include <qstyle.h>
50#include <qsize.h>
51#include <qevent.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>
58#endif
59
60QT_BEGIN_NAMESPACE
61
62class QTableCornerButton : public QAbstractButton
63{
64 Q_OBJECT
65public:
66 QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
67 void paintEvent(QPaintEvent*) {
68 QStyleOptionHeader opt;
69 opt.init(this);
70 QStyle::State state = QStyle::State_None;
71 if (isEnabled())
72 state |= QStyle::State_Enabled;
73 if (isActiveWindow())
74 state |= QStyle::State_Active;
75 if (isDown())
76 state |= QStyle::State_Sunken;
77 opt.state = state;
78 opt.rect = rect();
79 opt.position = QStyleOptionHeader::OnlyOneSection;
80 QPainter painter(this);
81 style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
82 }
83};
84
85void QTableViewPrivate::init()
86{
87 Q_Q(QTableView);
88
89 q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed);
90
91 QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
92 vertical->setClickable(true);
93 vertical->setHighlightSections(true);
94 q->setVerticalHeader(vertical);
95
96 QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
97 horizontal->setClickable(true);
98 horizontal->setHighlightSections(true);
99 q->setHorizontalHeader(horizontal);
100
101 tabKeyNavigation = true;
102
103 cornerWidget = new QTableCornerButton(q);
104 cornerWidget->setFocusPolicy(Qt::NoFocus);
105 QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll()));
106}
107
108/*!
109 \internal
110 Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections.
111*/
112void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
113{
114 Q_ASSERT(range && range->isValid());
115
116 int top = range->top();
117 int left = range->left();
118 int bottom = range->bottom();
119 int right = range->right();
120
121 while (bottom >= top && verticalHeader->isSectionHidden(bottom))
122 --bottom;
123 while (right >= left && horizontalHeader->isSectionHidden(right))
124 --right;
125
126 if (top > bottom || left > right) { // everything is hidden
127 *range = QItemSelectionRange();
128 return;
129 }
130
131 while (verticalHeader->isSectionHidden(top) && top <= bottom)
132 ++top;
133 while (horizontalHeader->isSectionHidden(left) && left <= right)
134 ++left;
135
136 if (top > bottom || left > right) { // everything is hidden
137 *range = QItemSelectionRange();
138 return;
139 }
140
141 QModelIndex bottomRight = model->index(bottom, right, range->parent());
142 QModelIndex topLeft = model->index(top, left, range->parent());
143 *range = QItemSelectionRange(topLeft, bottomRight);
144}
145
146/*!
147 \internal
148 Sets the span for the cell at (\a row, \a column).
149*/
150void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
151{
152 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
153 return;
154 Span sp(row, column, rowSpan, columnSpan);
155 QList<Span>::iterator it;
156 for (it = spans.begin(); it != spans.end(); ++it) {
157 if (((*it).top() == sp.top()) && ((*it).left() == sp.left())) {
158 if ((sp.height() == 1) && (sp.width() == 1))
159 spans.erase(it); // "Implicit" span (1, 1), no need to store it
160 else
161 *it = sp; // Replace
162 return;
163 }
164 }
165 spans.append(sp);
166}
167
168/*!
169 \internal
170 Gets the span information for the cell at (\a row, \a column).
171*/
172QTableViewPrivate::Span QTableViewPrivate::span(int row, int column) const
173{
174 QList<Span>::const_iterator it;
175 for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
176 Span span = *it;
177 if (isInSpan(row, column, span))
178 return span;
179 }
180 return Span(row, column, 1, 1);
181}
182
183/*!
184 \internal
185 Returns the logical index of the last section that's part of the span.
186*/
187int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
188{
189 int visual = header->visualIndex(logical);
190 for (int i = 1; i < span; ) {
191 if (++visual >= header->count())
192 break;
193 logical = header->logicalIndex(visual);
194 ++i;
195 }
196 return logical;
197}
198
199/*!
200 \internal
201 Returns the size of the span starting at logical index \a logical
202 and spanning \a span sections.
203*/
204int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const
205{
206 int endLogical = sectionSpanEndLogical(header, logical, span);
207 return header->sectionPosition(endLogical)
208 - header->sectionPosition(logical)
209 + header->sectionSize(endLogical);
210}
211
212/*!
213 \internal
214 Returns true if the section at logical index \a logical is part of the span
215 starting at logical index \a spanLogical and spanning \a span sections;
216 otherwise, returns false.
217*/
218bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
219{
220 if (logical == spanLogical)
221 return true; // it's the start of the span
222 int visual = header->visualIndex(spanLogical);
223 for (int i = 1; i < span; ) {
224 if (++visual >= header->count())
225 break;
226 spanLogical = header->logicalIndex(visual);
227 if (logical == spanLogical)
228 return true;
229 ++i;
230 }
231 return false;
232}
233
234/*!
235 \internal
236 Returns true if one or more spans intersect column \a column.
237*/
238bool QTableViewPrivate::spansIntersectColumn(int column) const
239{
240 QList<Span>::const_iterator it;
241 for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
242 Span span = *it;
243 if (spanContainsColumn(column, span.left(), span.width()))
244 return true;
245 }
246 return false;
247}
248
249/*!
250 \internal
251 Returns true if one or more spans intersect row \a row.
252*/
253bool QTableViewPrivate::spansIntersectRow(int row) const
254{
255 QList<Span>::const_iterator it;
256 for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
257 Span span = *it;
258 if (spanContainsRow(row, span.top(), span.height()))
259 return true;
260 }
261 return false;
262}
263
264/*!
265 \internal
266 Returns true if one or more spans intersect one or more columns.
267*/
268bool QTableViewPrivate::spansIntersectColumns(const QList<int> &columns) const
269{
270 QList<int>::const_iterator it;
271 for (it = columns.constBegin(); it != columns.constEnd(); ++it) {
272 if (spansIntersectColumn(*it))
273 return true;
274 }
275 return false;
276}
277
278/*!
279 \internal
280 Returns true if one or more spans intersect one or more rows.
281*/
282bool QTableViewPrivate::spansIntersectRows(const QList<int> &rows) const
283{
284 QList<int>::const_iterator it;
285 for (it = rows.constBegin(); it != rows.constEnd(); ++it) {
286 if (spansIntersectRow(*it))
287 return true;
288 }
289 return false;
290}
291
292/*!
293 \internal
294 Returns the visual rect for the given \a span.
295*/
296QRect QTableViewPrivate::visualSpanRect(const Span &span) const
297{
298 Q_Q(const QTableView);
299 // vertical
300 int row = span.top();
301 int rowp = verticalHeader->sectionViewportPosition(row);
302 int rowh = rowSpanHeight(row, span.height());
303 // horizontal
304 int column = span.left();
305 int colw = columnSpanWidth(column, span.width());
306 if (q->isRightToLeft())
307 column = span.right();
308 int colp = horizontalHeader->sectionViewportPosition(column);
309
310 const int i = showGrid ? 1 : 0;
311 if (q->isRightToLeft())
312 return QRect(colp + i, rowp, colw - i, rowh - i);
313 return QRect(colp, rowp, colw - i, rowh - i);
314}
315
316/*!
317 \internal
318 Draws the spanning cells within rect \a area, and clips them off as
319 preparation for the main drawing loop.
320 \a drawn is a QBitArray of visualRowCountxvisualCoulumnCount which say if particular cell has been drawn
321*/
322void QTableViewPrivate::drawAndClipSpans(const QRect &area, QPainter *painter,
323 const QStyleOptionViewItemV4 &option, QBitArray *drawn,
324 int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
325{
326 bool alternateBase = false;
327 QRegion region = viewport->rect();
328
329 QList<Span>::const_iterator it;
330 for (it = spans.constBegin(); it != spans.constEnd(); ++it) {
331 Span span = *it;
332
333 int row = span.top();
334 int col = span.left();
335 if (isHidden(row, col))
336 continue;
337 QModelIndex index = model->index(row, col, root);
338 if (!index.isValid())
339 continue;
340 QRect rect = visualSpanRect(span);
341 rect.translate(scrollDelayOffset);
342 if (!rect.intersects(area))
343 continue;
344 QStyleOptionViewItemV4 opt = option;
345 opt.rect = rect;
346 alternateBase = alternatingColors && (span.top() & 1);
347 if (alternateBase)
348 opt.features |= QStyleOptionViewItemV2::Alternate;
349 else
350 opt.features &= ~QStyleOptionViewItemV2::Alternate;
351 drawCell(painter, opt, index);
352 region -= rect;
353 for (int r = span.top(); r <= span.bottom(); ++r) {
354 const int vr = visualRow(r);
355 if (vr < firstVisualRow || vr > lastVisualRow)
356 continue;
357 for (int c = span.left(); c <= span.right(); ++c) {
358 const int vc = visualColumn(c);
359 if (vc < firstVisualColumn || vc > lastVisualColumn)
360 continue;
361 drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
362 + vc - firstVisualColumn);
363 }
364 }
365
366 }
367 painter->setClipRegion(region);
368}
369
370/*!
371 \internal
372 Draws a table cell.
373*/
374void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index)
375{
376 Q_Q(QTableView);
377 QStyleOptionViewItemV4 opt = option;
378
379 if (selectionModel && selectionModel->isSelected(index))
380 opt.state |= QStyle::State_Selected;
381 if (index == hover)
382 opt.state |= QStyle::State_MouseOver;
383 if (option.state & QStyle::State_Enabled) {
384 QPalette::ColorGroup cg;
385 if ((model->flags(index) & Qt::ItemIsEnabled) == 0) {
386 opt.state &= ~QStyle::State_Enabled;
387 cg = QPalette::Disabled;
388 } else {
389 cg = QPalette::Normal;
390 }
391 opt.palette.setCurrentColorGroup(cg);
392 }
393
394 if (index == q->currentIndex()) {
395 const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
396 if (focus)
397 opt.state |= QStyle::State_HasFocus;
398 }
399
400 if (opt.features & QStyleOptionViewItemV2::Alternate)
401 painter->fillRect(opt.rect, opt.palette.brush(QPalette::AlternateBase));
402
403 if (const QWidget *widget = editorForIndex(index).editor) {
404 painter->save();
405 painter->setClipRect(widget->geometry());
406 q->itemDelegate(index)->paint(painter, opt, index);
407 painter->restore();
408 } else {
409 q->itemDelegate(index)->paint(painter, opt, index);
410 }
411}
412
413/*!
414 \class QTableView
415
416 \brief The QTableView class provides a default model/view
417 implementation of a table view.
418
419 \ingroup model-view
420 \ingroup advanced
421 \mainclass
422
423 A QTableView implements a table view that displays items from a
424 model. This class is used to provide standard tables that were
425 previously provided by the QTable class, but using the more
426 flexible approach provided by Qt's model/view architecture.
427
428 The QTableView class is one of the \l{Model/View Classes}
429 and is part of Qt's \l{Model/View Programming}{model/view framework}.
430
431 QTableView implements the interfaces defined by the
432 QAbstractItemView class to allow it to display data provided by
433 models derived from the QAbstractItemModel class.
434
435 \section1 Navigation
436
437 You can navigate the cells in the table by clicking on a cell with the
438 mouse, or by using the arrow keys. Because QTableView enables
439 \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you
440 can also hit Tab and Backtab to move from cell to cell.
441
442 \section1 Visual Appearance
443
444 The table has a vertical header that can be obtained using the
445 verticalHeader() function, and a horizontal header that is available
446 through the horizontalHeader() function. The height of each row in the
447 table can be found by using rowHeight(); similarly, the width of
448 columns can be found using columnWidth(). Since both of these are plain
449 widgets, you can hide either of them using their hide() functions.
450
451 Rows and columns can be hidden and shown with hideRow(), hideColumn(),
452 showRow(), and showColumn(). They can be selected with selectRow()
453 and selectColumn(). The table will show a grid depending on the
454 \l showGrid property.
455
456 The items shown in a table view, like those in the other item views, are
457 rendered and edited using standard \l{QItemDelegate}{delegates}. However,
458 for some tasks it is sometimes useful to be able to insert widgets in a
459 table instead. Widgets are set for particular indexes with the
460 \l{QAbstractItemView::}{setIndexWidget()} function, and
461 later retrieved with \l{QAbstractItemView::}{indexWidget()}.
462
463 \table
464 \row \o \inlineimage qtableview-resized.png
465 \o By default, the cells in a table do not expand to fill the available space.
466
467 You can make the cells fill the available space by stretching the last
468 header section. Access the relevant header using horizontalHeader()
469 or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection}
470 property.
471
472 To distribute the available space according to the space requirement of
473 each column or row, call the view's resizeColumnsToContents() or
474 resizeRowsToContents() functions.
475 \endtable
476
477 \section1 Coordinate Systems
478
479 For some specialized forms of tables it is useful to be able to
480 convert between row and column indexes and widget coordinates.
481 The rowAt() function provides the y-coordinate within the view of the
482 specified row; the row index can be used to obtain a corresponding
483 y-coordinate with rowViewportPosition(). The columnAt() and
484 columnViewportPosition() functions provide the equivalent conversion
485 operations between x-coordinates and column indexes.
486
487 \section1 Styles
488
489 QTableView is styled appropriately for each platform. The following images show
490 how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see
491 its appearance in other styles.
492
493 \table 100%
494 \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view
495 \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view
496 \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view
497 \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view.
498 \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view.
499 \o A \l{Plastique Style Widget Gallery}{Plastique style} table view.
500 \endtable
501
502 \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
503 {Chart Example}, {Pixelator Example}, {Table Model Example}
504*/
505
506/*!
507 Constructs a table view with a \a parent to represent the data.
508
509 \sa QAbstractItemModel
510*/
511
512QTableView::QTableView(QWidget *parent)
513 : QAbstractItemView(*new QTableViewPrivate, parent)
514{
515 Q_D(QTableView);
516 d->init();
517}
518
519/*!
520 \internal
521*/
522QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent)
523 : QAbstractItemView(dd, parent)
524{
525 Q_D(QTableView);
526 d->init();
527}
528
529/*!
530 Destroys the table view.
531*/
532QTableView::~QTableView()
533{
534}
535
536/*!
537 \reimp
538*/
539void QTableView::setModel(QAbstractItemModel *model)
540{
541 Q_D(QTableView);
542 d->verticalHeader->setModel(model);
543 d->horizontalHeader->setModel(model);
544 QAbstractItemView::setModel(model);
545}
546
547/*!
548 \reimp
549*/
550void QTableView::setRootIndex(const QModelIndex &index)
551{
552 Q_D(QTableView);
553 if (index == d->root) {
554 viewport()->update();
555 return;
556 }
557 d->verticalHeader->setRootIndex(index);
558 d->horizontalHeader->setRootIndex(index);
559 QAbstractItemView::setRootIndex(index);
560}
561
562/*!
563 \reimp
564*/
565void QTableView::setSelectionModel(QItemSelectionModel *selectionModel)
566{
567 Q_D(QTableView);
568 Q_ASSERT(selectionModel);
569 d->verticalHeader->setSelectionModel(selectionModel);
570 d->horizontalHeader->setSelectionModel(selectionModel);
571 QAbstractItemView::setSelectionModel(selectionModel);
572}
573
574/*!
575 Returns the table view's horizontal header.
576
577 \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData()
578*/
579QHeaderView *QTableView::horizontalHeader() const
580{
581 Q_D(const QTableView);
582 return d->horizontalHeader;
583}
584
585/*!
586 Returns the table view's vertical header.
587
588 \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData()
589*/
590QHeaderView *QTableView::verticalHeader() const
591{
592 Q_D(const QTableView);
593 return d->verticalHeader;
594}
595
596/*!
597 Sets the widget to use for the horizontal header to \a header.
598
599 \sa horizontalHeader() setVerticalHeader()
600*/
601void QTableView::setHorizontalHeader(QHeaderView *header)
602{
603 Q_D(QTableView);
604
605 if (!header || header == d->horizontalHeader)
606 return;
607 if (d->horizontalHeader && d->horizontalHeader->parent() == this)
608 delete d->horizontalHeader;
609 d->horizontalHeader = header;
610 d->horizontalHeader->setParent(this);
611 if (!d->horizontalHeader->model()) {
612 d->horizontalHeader->setModel(d->model);
613 if (d->selectionModel)
614 d->horizontalHeader->setSelectionModel(d->selectionModel);
615 }
616
617 connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)),
618 this, SLOT(columnResized(int,int,int)));
619 connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)),
620 this, SLOT(columnMoved(int,int,int)));
621 connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)),
622 this, SLOT(columnCountChanged(int,int)));
623 connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
624 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int)));
625 connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
626 this, SLOT(resizeColumnToContents(int)));
627 connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
628}
629
630/*!
631 Sets the widget to use for the vertical header to \a header.
632
633 \sa verticalHeader() setHorizontalHeader()
634*/
635void QTableView::setVerticalHeader(QHeaderView *header)
636{
637 Q_D(QTableView);
638
639 if (!header || header == d->verticalHeader)
640 return;
641 if (d->verticalHeader && d->verticalHeader->parent() == this)
642 delete d->verticalHeader;
643 d->verticalHeader = header;
644 d->verticalHeader->setParent(this);
645 if (!d->verticalHeader->model()) {
646 d->verticalHeader->setModel(d->model);
647 if (d->selectionModel)
648 d->verticalHeader->setSelectionModel(d->selectionModel);
649 }
650
651 connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)),
652 this, SLOT(rowResized(int,int,int)));
653 connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)),
654 this, SLOT(rowMoved(int,int,int)));
655 connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)),
656 this, SLOT(rowCountChanged(int,int)));
657 connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
658 connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int)));
659 connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
660 this, SLOT(resizeRowToContents(int)));
661 connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
662}
663
664/*!
665 \internal
666
667 Scroll the contents of the table view by (\a dx, \a dy).
668*/
669void QTableView::scrollContentsBy(int dx, int dy)
670{
671 Q_D(QTableView);
672
673 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
674
675 dx = isRightToLeft() ? -dx : dx;
676 if (dx) {
677 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
678 int oldOffset = d->horizontalHeader->offset();
679 if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
680 d->horizontalHeader->setOffsetToLastSection();
681 else
682 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
683 int newOffset = d->horizontalHeader->offset();
684 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
685 } else {
686 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
687 }
688 }
689 if (dy) {
690 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
691 int oldOffset = d->verticalHeader->offset();
692 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
693 d->verticalHeader->setOffsetToLastSection();
694 else
695 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
696 int newOffset = d->verticalHeader->offset();
697 dy = oldOffset - newOffset;
698 } else {
699 d->verticalHeader->setOffset(verticalScrollBar()->value());
700 }
701 }
702 d->scrollContentsBy(dx, dy);
703
704 if (d->showGrid) {
705 //we need to update the first line of the previous top item in the view
706 //because it has the grid drawn if the header is invisible.
707 //It is strictly related to what's done at then end of the paintEvent
708 if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) {
709 d->viewport->update(0, dy, d->viewport->width(), dy);
710 }
711 if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) {
712 d->viewport->update(dx, 0, dx, d->viewport->height());
713 }
714 }
715}
716
717/*!
718 \reimp
719*/
720QStyleOptionViewItem QTableView::viewOptions() const
721{
722 QStyleOptionViewItem option = QAbstractItemView::viewOptions();
723 option.showDecorationSelected = true;
724 return option;
725}
726
727/*!
728 Paints the table on receipt of the given paint event \a event.
729*/
730void QTableView::paintEvent(QPaintEvent *event)
731{
732 Q_D(QTableView);
733 // setup temp variables for the painting
734 QStyleOptionViewItemV4 option = d->viewOptionsV4();
735 const QPoint offset = d->scrollDelayOffset;
736 const bool showGrid = d->showGrid;
737 const int gridSize = showGrid ? 1 : 0;
738 const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
739 const QColor gridColor = static_cast<QRgb>(gridHint);
740 const QPen gridPen = QPen(gridColor, 0, d->gridStyle);
741 const QHeaderView *verticalHeader = d->verticalHeader;
742 const QHeaderView *horizontalHeader = d->horizontalHeader;
743 const QStyle::State state = option.state;
744 const bool alternate = d->alternatingColors;
745 const bool rightToLeft = isRightToLeft();
746
747 QPainter painter(d->viewport);
748
749 // if there's nothing to do, clear the area and return
750 if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
751 return;
752
753 uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
754 uint y = verticalHeader->length() - verticalHeader->offset() - 1;
755
756 QVector<QRect> rects = event->region().rects();
757
758 //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
759 //same goes for ...VisualColumn
760 int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
761 int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height());
762 if (lastVisualRow == -1)
763 lastVisualRow = d->model->rowCount(d->root) - 1;
764
765 int firstVisualColumn = horizontalHeader->visualIndexAt(0);
766 int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width());
767 if (rightToLeft)
768 qSwap(firstVisualColumn, lastVisualColumn);
769 if (firstVisualColumn == -1)
770 firstVisualColumn = 0;
771 if (lastVisualColumn == -1)
772 lastVisualColumn = horizontalHeader->count() - 1;
773
774 QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
775
776 for (int i = 0; i < rects.size(); ++i) {
777 QRect dirtyArea = rects.at(i);
778 dirtyArea.translate(offset);
779 dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
780 if (rightToLeft) {
781 dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
782 } else {
783 dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
784 }
785
786 if (d->hasSpans())
787 d->drawAndClipSpans(dirtyArea, &painter, option, &drawn,
788 firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
789
790 // get the horizontal start and end visual sections
791 int left = horizontalHeader->visualIndexAt(dirtyArea.left());
792 int right = horizontalHeader->visualIndexAt(dirtyArea.right());
793 if (rightToLeft)
794 qSwap(left, right);
795 if (left == -1) left = 0;
796 if (right == -1) right = horizontalHeader->count() - 1;
797
798 // get the vertical start and end visual sections and if alternate color
799 int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
800 if (bottom == -1) bottom = verticalHeader->count() - 1;
801 int top = 0;
802 bool alternateBase = false;
803 if (alternate && verticalHeader->sectionsHidden()) {
804 uint verticalOffset = verticalHeader->offset();
805 int row = verticalHeader->logicalIndex(top);
806 for (int y = 0;
807 ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);
808 ++top) {
809 row = verticalHeader->logicalIndex(top);
810 if (alternate && !verticalHeader->isSectionHidden(row))
811 alternateBase = !alternateBase;
812 }
813 } else {
814 top = verticalHeader->visualIndexAt(dirtyArea.top());
815 alternateBase = (top & 1) && alternate;
816 }
817 if (top == -1 || top > bottom)
818 continue;
819
820 // Paint each row item
821 for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
822 int row = verticalHeader->logicalIndex(visualRowIndex);
823 if (verticalHeader->isSectionHidden(row))
824 continue;
825 int rowY = rowViewportPosition(row);
826 rowY += offset.y();
827 int rowh = rowHeight(row) - gridSize;
828
829 // Paint each column item
830 for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
831 int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
832 + visualColumnIndex - firstVisualColumn;
833
834 if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
835 continue;
836 drawn.setBit(currentBit);
837
838 int col = horizontalHeader->logicalIndex(visualColumnIndex);
839 if (horizontalHeader->isSectionHidden(col))
840 continue;
841 int colp = columnViewportPosition(col);
842 colp += offset.x();
843 int colw = columnWidth(col) - gridSize;
844
845 const QModelIndex index = d->model->index(row, col, d->root);
846 if (index.isValid()) {
847 option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
848 if (alternate) {
849 if (alternateBase)
850 option.features |= QStyleOptionViewItemV2::Alternate;
851 else
852 option.features &= ~QStyleOptionViewItemV2::Alternate;
853 }
854 d->drawCell(&painter, option, index);
855 }
856 }
857 alternateBase = !alternateBase && alternate;
858 }
859
860 if (showGrid) {
861 // Find the bottom right (the last rows/coloumns might be hidden)
862 while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;
863 QPen old = painter.pen();
864 painter.setPen(gridPen);
865 // Paint each row
866 for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
867 int row = verticalHeader->logicalIndex(visualIndex);
868 if (verticalHeader->isSectionHidden(row))
869 continue;
870 int rowY = rowViewportPosition(row);
871 rowY += offset.y();
872 int rowh = rowHeight(row) - gridSize;
873 painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
874 }
875
876 // Paint each column
877 for (int h = left; h <= right; ++h) {
878 int col = horizontalHeader->logicalIndex(h);
879 if (horizontalHeader->isSectionHidden(col))
880 continue;
881 int colp = columnViewportPosition(col);
882 colp += offset.x();
883 if (!rightToLeft)
884 colp += columnWidth(col) - gridSize;
885 painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
886 }
887
888 //draw the top & left grid lines if the headers are not visible.
889 //We do update this line when subsequent scroll happen (see scrollContentsBy)
890 if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem)
891 painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0);
892 if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem)
893 painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom());
894 painter.setPen(old);
895 }
896 }
897
898#ifndef QT_NO_DRAGANDDROP
899 // Paint the dropIndicator
900 d->paintDropIndicator(&painter);
901#endif
902}
903
904/*!
905 Returns the index position of the model item corresponding to the
906 table item at position \a pos in contents coordinates.
907*/
908QModelIndex QTableView::indexAt(const QPoint &pos) const
909{
910 Q_D(const QTableView);
911 d->executePostedLayout();
912 int r = rowAt(pos.y());
913 int c = columnAt(pos.x());
914 if (r >= 0 && c >= 0) {
915 if (d->hasSpans()) {
916 QTableViewPrivate::Span span = d->span(r, c);
917 r = span.top();
918 c = span.left();
919 }
920 return d->model->index(r, c, d->root);
921 }
922 return QModelIndex();
923}
924
925/*!
926 Returns the horizontal offset of the items in the table view.
927
928 Note that the table view uses the horizontal header section
929 positions to determine the positions of columns in the view.
930
931 \sa verticalOffset()
932*/
933int QTableView::horizontalOffset() const
934{
935 Q_D(const QTableView);
936 return d->horizontalHeader->offset();
937}
938
939/*!
940 Returns the vertical offset of the items in the table view.
941
942 Note that the table view uses the vertical header section
943 positions to determine the positions of rows in the view.
944
945 \sa horizontalOffset()
946*/
947int QTableView::verticalOffset() const
948{
949 Q_D(const QTableView);
950 return d->verticalHeader->offset();
951}
952
953/*!
954 \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
955
956 Moves the cursor in accordance with the given \a cursorAction, using the
957 information provided by the \a modifiers.
958
959 \sa QAbstractItemView::CursorAction
960*/
961QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
962{
963 Q_D(QTableView);
964 Q_UNUSED(modifiers);
965
966 int bottom = d->model->rowCount(d->root) - 1;
967 // make sure that bottom is the bottommost *visible* row
968 while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
969 --bottom;
970
971 int right = d->model->columnCount(d->root) - 1;
972
973 while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
974 --right;
975
976 if (bottom == -1 || right == -1)
977 return QModelIndex(); // model is empty
978
979 QModelIndex current = currentIndex();
980
981 if (!current.isValid()) {
982 int row = 0;
983 int column = 0;
984 while (column < right && isColumnHidden(d->logicalColumn(column)))
985 ++column;
986 while (isRowHidden(d->logicalRow(row)) && row < bottom)
987 ++row;
988 return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
989 }
990
991 int visualRow = d->visualRow(current.row());
992 Q_ASSERT(visualRow != -1);
993 int visualColumn = d->visualColumn(current.column());
994 Q_ASSERT(visualColumn != -1);
995
996 if (isRightToLeft()) {
997 if (cursorAction == MoveLeft)
998 cursorAction = MoveRight;
999 else if (cursorAction == MoveRight)
1000 cursorAction = MoveLeft;
1001 }
1002
1003 switch (cursorAction) {
1004 case MoveUp:
1005#ifdef QT_KEYPAD_NAVIGATION
1006 if (QApplication::keypadNavigationEnabled() && visualRow == 0)
1007 visualRow = d->visualRow(model()->rowCount() - 1) + 1;
1008#endif
1009 --visualRow;
1010 while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1011 --visualRow;
1012 if (d->hasSpans()) {
1013 int row = d->logicalRow(visualRow);
1014 QTableViewPrivate::Span span = d->span(row, current.column());
1015 visualRow = d->visualRow(span.top());
1016 visualColumn = d->visualColumn(span.left());
1017 }
1018 break;
1019 case MoveDown:
1020 if (d->hasSpans()) {
1021 QTableViewPrivate::Span span = d->span(current.row(), current.column());
1022 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1023 }
1024#ifdef QT_KEYPAD_NAVIGATION
1025 if (QApplication::keypadNavigationEnabled() && visualRow >= bottom)
1026 visualRow = -1;
1027#endif
1028 ++visualRow;
1029 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1030 ++visualRow;
1031 if (d->hasSpans()) {
1032 int row = d->logicalRow(visualRow);
1033 QTableViewPrivate::Span span = d->span(row, current.column());
1034 visualColumn = d->visualColumn(span.left());
1035 }
1036 break;
1037 case MovePrevious: {
1038 int left = 0;
1039 while (d->isVisualColumnHiddenOrDisabled(visualRow, left) && left < right)
1040 ++left;
1041 if (visualColumn == left) {
1042 visualColumn = right;
1043 int top = 0;
1044 while (top < bottom && d->isVisualRowHiddenOrDisabled(top, visualColumn))
1045 ++top;
1046 if (visualRow == top)
1047 visualRow = bottom;
1048 else
1049 --visualRow;
1050 while (visualRow > 0 && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1051 --visualRow;
1052 break;
1053 } // else MoveLeft
1054 }
1055 case MoveLeft:
1056 --visualColumn;
1057 while (visualColumn > 0 && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1058 --visualColumn;
1059 if (d->hasSpans()) {
1060 int column = d->logicalColumn(visualColumn);
1061 QTableViewPrivate::Span span = d->span(current.row(), column);
1062 visualRow = d->visualRow(span.top());
1063 visualColumn = d->visualColumn(span.left());
1064 }
1065 break;
1066 case MoveNext:
1067 if (visualColumn == right) {
1068 visualColumn = 0;
1069 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1070 ++visualColumn;
1071 if (visualRow == bottom)
1072 visualRow = 0;
1073 else
1074 ++visualRow;
1075 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1076 ++visualRow;
1077 break;
1078 } // else MoveRight
1079 case MoveRight:
1080 if (d->hasSpans()) {
1081 QTableViewPrivate::Span span = d->span(current.row(), current.column());
1082 visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1083 }
1084 ++visualColumn;
1085 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1086 ++visualColumn;
1087 if (d->hasSpans()) {
1088 int column = d->logicalColumn(visualColumn);
1089 QTableViewPrivate::Span span = d->span(current.row(), column);
1090 visualRow = d->visualRow(span.top());
1091 }
1092 break;
1093 case MoveHome:
1094 visualColumn = 0;
1095 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1096 ++visualColumn;
1097 if (modifiers & Qt::ControlModifier) {
1098 visualRow = 0;
1099 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1100 ++visualRow;
1101 }
1102 break;
1103 case MoveEnd:
1104 visualColumn = right;
1105 if (modifiers & Qt::ControlModifier)
1106 visualRow = bottom;
1107 break;
1108 case MovePageUp: {
1109 int top = 0;
1110 while (top < bottom && d->isVisualRowHiddenOrDisabled(top, visualColumn))
1111 ++top;
1112 int newRow = qMax(rowAt(visualRect(current).top() - d->viewport->height()), top);
1113 return d->model->index(qBound(0, newRow, bottom), current.column(), d->root);
1114 }
1115 case MovePageDown: {
1116 int newRow = qMin(rowAt(visualRect(current).bottom() + d->viewport->height()), bottom);
1117 if (newRow < 0)
1118 newRow = bottom;
1119 return d->model->index(qBound(0, newRow, bottom), current.column(), d->root);
1120 }}
1121
1122 int logicalRow = d->logicalRow(visualRow);
1123 int logicalColumn = d->logicalColumn(visualColumn);
1124 if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
1125 return QModelIndex();
1126
1127 QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
1128 if (!isIndexHidden(result) && d->isIndexEnabled(result))
1129 return d->model->index(logicalRow, logicalColumn, d->root);
1130
1131 return QModelIndex();
1132}
1133
1134/*!
1135 \fn void QTableView::setSelection(const QRect &rect,
1136 QItemSelectionModel::SelectionFlags flags)
1137
1138 Selects the items within the given \a rect and in accordance with
1139 the specified selection \a flags.
1140*/
1141void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1142{
1143 Q_D(QTableView);
1144 QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
1145 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
1146 QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
1147 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
1148 if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
1149 return;
1150
1151 bool verticalMoved = verticalHeader()->sectionsMoved();
1152 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1153
1154 QItemSelection selection;
1155
1156 if (d->hasSpans()) {
1157 bool expanded;
1158 int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
1159 int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1160 int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
1161 int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1162 do {
1163 expanded = false;
1164 QList<QTableViewPrivate::Span>::const_iterator it;
1165 for (it = d->spans.constBegin(); it != d->spans.constEnd(); ++it) {
1166 QTableViewPrivate::Span span = *it;
1167 int t = d->visualRow(span.top());
1168 int l = d->visualColumn(span.left());
1169 int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1170 int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1171 if ((t > bottom) || (l > right) || (top > b) || (left > r))
1172 continue; // no intersect
1173 if (t < top) {
1174 top = t;
1175 expanded = true;
1176 }
1177 if (l < left) {
1178 left = l;
1179 expanded = true;
1180 }
1181 if (b > bottom) {
1182 bottom = b;
1183 expanded = true;
1184 }
1185 if (r > right) {
1186 right = r;
1187 expanded = true;
1188 }
1189 if (expanded)
1190 break;
1191 }
1192 } while (expanded);
1193 for (int horizontal = left; horizontal <= right; ++horizontal) {
1194 int column = d->logicalColumn(horizontal);
1195 for (int vertical = top; vertical <= bottom; ++vertical) {
1196 int row = d->logicalRow(vertical);
1197 QModelIndex index = d->model->index(row, column, d->root);
1198 selection.append(QItemSelectionRange(index));
1199 }
1200 }
1201 } else if (verticalMoved && horizontalMoved) {
1202 int top = d->visualRow(tl.row());
1203 int left = d->visualColumn(tl.column());
1204 int bottom = d->visualRow(br.row());
1205 int right = d->visualColumn(br.column());
1206 for (int horizontal = left; horizontal <= right; ++horizontal) {
1207 int column = d->logicalColumn(horizontal);
1208 for (int vertical = top; vertical <= bottom; ++vertical) {
1209 int row = d->logicalRow(vertical);
1210 QModelIndex index = d->model->index(row, column, d->root);
1211 selection.append(QItemSelectionRange(index));
1212 }
1213 }
1214 } else if (horizontalMoved) {
1215 int left = d->visualColumn(tl.column());
1216 int right = d->visualColumn(br.column());
1217 for (int visual = left; visual <= right; ++visual) {
1218 int column = d->logicalColumn(visual);
1219 QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
1220 QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
1221 selection.append(QItemSelectionRange(topLeft, bottomRight));
1222 }
1223 } else if (verticalMoved) {
1224 int top = d->visualRow(tl.row());
1225 int bottom = d->visualRow(br.row());
1226 for (int visual = top; visual <= bottom; ++visual) {
1227 int row = d->logicalRow(visual);
1228 QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
1229 QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
1230 selection.append(QItemSelectionRange(topLeft, bottomRight));
1231 }
1232 } else { // nothing moved
1233 selection.append(QItemSelectionRange(tl, br));
1234 }
1235
1236 d->selectionModel->select(selection, command);
1237}
1238
1239/*!
1240 \internal
1241
1242 Returns the rectangle from the viewport of the items in the given
1243 \a selection.
1244*/
1245QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const
1246{
1247 Q_D(const QTableView);
1248
1249 if (selection.isEmpty())
1250 return QRegion();
1251
1252 QRegion selectionRegion;
1253 bool verticalMoved = verticalHeader()->sectionsMoved();
1254 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1255
1256 if ((verticalMoved && horizontalMoved) || d->hasSpans()) {
1257 for (int i = 0; i < selection.count(); ++i) {
1258 QItemSelectionRange range = selection.at(i);
1259 if (range.parent() != d->root || !range.isValid())
1260 continue;
1261 for (int r = range.top(); r <= range.bottom(); ++r)
1262 for (int c = range.left(); c <= range.right(); ++c)
1263 selectionRegion += QRegion(visualRect(d->model->index(r, c, d->root)));
1264 }
1265 } else if (horizontalMoved) {
1266 for (int i = 0; i < selection.count(); ++i) {
1267 QItemSelectionRange range = selection.at(i);
1268 if (range.parent() != d->root || !range.isValid())
1269 continue;
1270 int top = rowViewportPosition(range.top());
1271 int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1272 if (top > bottom)
1273 qSwap<int>(top, bottom);
1274 int height = bottom - top;
1275 for (int c = range.left(); c <= range.right(); ++c)
1276 selectionRegion += QRegion(QRect(columnViewportPosition(c), top,
1277 columnWidth(c), height));
1278 }
1279 } else if (verticalMoved) {
1280 for (int i = 0; i < selection.count(); ++i) {
1281 QItemSelectionRange range = selection.at(i);
1282 if (range.parent() != d->root || !range.isValid())
1283 continue;
1284 int left = columnViewportPosition(range.left());
1285 int right = columnViewportPosition(range.right()) + columnWidth(range.right());
1286 if (left > right)
1287 qSwap<int>(left, right);
1288 int width = right - left;
1289 for (int r = range.top(); r <= range.bottom(); ++r)
1290 selectionRegion += QRegion(QRect(left, rowViewportPosition(r),
1291 width, rowHeight(r)));
1292 }
1293 } else { // nothing moved
1294 for (int i = 0; i < selection.count(); ++i) {
1295 QItemSelectionRange range = selection.at(i);
1296 if (range.parent() != d->root || !range.isValid())
1297 continue;
1298 d->trimHiddenSelections(&range);
1299 QRect tl = visualRect(range.topLeft());
1300 QRect br = visualRect(range.bottomRight());
1301 selectionRegion += QRegion(tl|br);
1302 }
1303 }
1304
1305 return selectionRegion;
1306}
1307
1308
1309/*!
1310 \reimp
1311*/
1312QModelIndexList QTableView::selectedIndexes() const
1313{
1314 Q_D(const QTableView);
1315 QModelIndexList viewSelected;
1316 QModelIndexList modelSelected;
1317 if (d->selectionModel)
1318 modelSelected = d->selectionModel->selectedIndexes();
1319 for (int i = 0; i < modelSelected.count(); ++i) {
1320 QModelIndex index = modelSelected.at(i);
1321 if (!isIndexHidden(index) && index.parent() == d->root)
1322 viewSelected.append(index);
1323 }
1324 return viewSelected;
1325}
1326
1327
1328/*!
1329 This slot is called whenever rows are added or deleted. The
1330 previous number of rows is specified by \a oldCount, and the new
1331 number of rows is specified by \a newCount.
1332*/
1333void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ )
1334{
1335 Q_D(QTableView);
1336 updateGeometries();
1337 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem)
1338 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1339 else
1340 d->verticalHeader->setOffset(verticalScrollBar()->value());
1341 d->viewport->update();
1342}
1343
1344/*!
1345 This slot is called whenever columns are added or deleted. The
1346 previous number of columns is specified by \a oldCount, and the new
1347 number of columns is specified by \a newCount.
1348*/
1349void QTableView::columnCountChanged(int, int)
1350{
1351 Q_D(QTableView);
1352 updateGeometries();
1353 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
1354 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
1355 else
1356 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
1357 d->viewport->update();
1358}
1359
1360/*!
1361 \reimp
1362*/
1363void QTableView::updateGeometries()
1364{
1365 Q_D(QTableView);
1366 if (d->geometryRecursionBlock)
1367 return;
1368 d->geometryRecursionBlock = true;
1369
1370 int width = 0;
1371 if (!d->verticalHeader->isHidden()) {
1372 width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
1373 width = qMin(width, d->verticalHeader->maximumWidth());
1374 }
1375 int height = 0;
1376 if (!d->horizontalHeader->isHidden()) {
1377 height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
1378 height = qMin(height, d->horizontalHeader->maximumHeight());
1379 }
1380 bool reverse = isRightToLeft();
1381 if (reverse)
1382 setViewportMargins(0, height, width, 0);
1383 else
1384 setViewportMargins(width, height, 0, 0);
1385
1386 // update headers
1387
1388 QRect vg = d->viewport->geometry();
1389
1390 int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
1391 d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
1392 if (d->verticalHeader->isHidden())
1393 QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
1394
1395 int horizontalTop = vg.top() - height;
1396 d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
1397 if (d->horizontalHeader->isHidden())
1398 QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
1399
1400 // update cornerWidget
1401 if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
1402 d->cornerWidget->setHidden(true);
1403 } else {
1404 d->cornerWidget->setHidden(false);
1405 d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
1406 }
1407
1408 // update scroll bars
1409
1410 // ### move this block into the if
1411 QSize vsize = d->viewport->size();
1412 QSize max = maximumViewportSize();
1413 uint horizontalLength = d->horizontalHeader->length();
1414 uint verticalLength = d->verticalHeader->length();
1415 if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength)
1416 vsize = max;
1417
1418 // horizontal scroll bar
1419 const int columnCount = d->horizontalHeader->count();
1420 const int viewportWidth = vsize.width();
1421 int columnsInViewport = 0;
1422 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
1423 int logical = d->horizontalHeader->logicalIndex(column);
1424 if (!d->horizontalHeader->isSectionHidden(logical)) {
1425 width += d->horizontalHeader->sectionSize(logical);
1426 if (width > viewportWidth)
1427 break;
1428 ++columnsInViewport;
1429 }
1430 }
1431 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
1432 const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
1433 horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
1434 horizontalScrollBar()->setPageStep(columnsInViewport);
1435 if (columnsInViewport >= visibleColumns)
1436 d->horizontalHeader->setOffset(0);
1437 horizontalScrollBar()->setSingleStep(1);
1438 } else { // ScrollPerPixel
1439 horizontalScrollBar()->setPageStep(vsize.width());
1440 horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
1441 horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
1442 }
1443
1444 // vertical scroll bar
1445 const int rowCount = d->verticalHeader->count();
1446 const int viewportHeight = vsize.height();
1447 int rowsInViewport = 0;
1448 for (int height = 0, row = rowCount - 1; row >= 0; --row) {
1449 int logical = d->verticalHeader->logicalIndex(row);
1450 if (!d->verticalHeader->isSectionHidden(logical)) {
1451 height += d->verticalHeader->sectionSize(logical);
1452 if (height > viewportHeight)
1453 break;
1454 ++rowsInViewport;
1455 }
1456 }
1457 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1458 const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
1459 verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
1460 verticalScrollBar()->setPageStep(rowsInViewport);
1461 if (rowsInViewport >= visibleRows)
1462 d->verticalHeader->setOffset(0);
1463 verticalScrollBar()->setSingleStep(1);
1464 } else { // ScrollPerPixel
1465 verticalScrollBar()->setPageStep(vsize.height());
1466 verticalScrollBar()->setRange(0, verticalLength - vsize.height());
1467 verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
1468 }
1469
1470 d->geometryRecursionBlock = false;
1471 QAbstractItemView::updateGeometries();
1472}
1473
1474/*!
1475 Returns the size hint for the given \a row's height or -1 if there
1476 is no model.
1477
1478 If you need to set the height of a given row to a fixed value, call
1479 QHeaderView::resizeSection() on the table's vertical header.
1480
1481 If you reimplement this function in a subclass, note that the value you
1482 return is only used when resizeRowToContents() is called. In that case,
1483 if a larger row height is required by either the vertical header or
1484 the item delegate, that width will be used instead.
1485
1486 \sa QWidget::sizeHint, verticalHeader()
1487*/
1488int QTableView::sizeHintForRow(int row) const
1489{
1490 Q_D(const QTableView);
1491
1492 if (!model())
1493 return -1;
1494
1495 int left = qMax(0, columnAt(0));
1496 int right = columnAt(d->viewport->width());
1497 if (right == -1) // the table don't have enough columns to fill the viewport
1498 right = d->model->columnCount(d->root) - 1;
1499
1500 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1501
1502 int hint = 0;
1503 QModelIndex index;
1504 for (int column = left; column <= right; ++column) {
1505 int logicalColumn = d->horizontalHeader->logicalIndex(column);
1506 if (d->horizontalHeader->isSectionHidden(logicalColumn))
1507 continue;
1508 index = d->model->index(row, logicalColumn, d->root);
1509 if (d->wrapItemText) {// for wrapping boundaries
1510 option.rect.setY(rowViewportPosition(index.row()));
1511 option.rect.setHeight(rowHeight(index.row()));
1512 option.rect.setX(columnViewportPosition(index.column()));
1513 option.rect.setWidth(columnWidth(index.column()));
1514 }
1515
1516 QWidget *editor = d->editorForIndex(index).editor;
1517 if (editor && d->persistent.contains(editor)) {
1518 hint = qMax(hint, editor->sizeHint().height());
1519 int min = editor->minimumSize().height();
1520 int max = editor->maximumSize().height();
1521 hint = qBound(min, hint, max);
1522 }
1523
1524 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height());
1525 }
1526
1527 return d->showGrid ? hint + 1 : hint;
1528}
1529
1530/*!
1531 Returns the size hint for the given \a column's width or -1 if
1532 there is no model.
1533
1534 If you need to set the width of a given column to a fixed value, call
1535 QHeaderView::resizeSection() on the table's horizontal header.
1536
1537 If you reimplement this function in a subclass, note that the value you
1538 return will be used when resizeColumnToContents() or
1539 QHeaderView::resizeSections() is called. If a larger column width is
1540 required by either the horizontal header or the item delegate, the larger
1541 width will be used instead.
1542
1543 \sa QWidget::sizeHint, horizontalHeader()
1544*/
1545int QTableView::sizeHintForColumn(int column) const
1546{
1547 Q_D(const QTableView);
1548
1549 if (!model())
1550 return -1;
1551
1552 int top = qMax(0, rowAt(0));
1553 int bottom = rowAt(d->viewport->height());
1554 if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
1555 bottom = d->model->rowCount(d->root) - 1;
1556
1557 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1558
1559 int hint = 0;
1560 QModelIndex index;
1561 for (int row = top; row <= bottom; ++row) {
1562 int logicalRow = d->verticalHeader->logicalIndex(row);
1563 if (d->verticalHeader->isSectionHidden(logicalRow))
1564 continue;
1565 index = d->model->index(logicalRow, column, d->root);
1566
1567 QWidget *editor = d->editorForIndex(index).editor;
1568 if (editor && d->persistent.contains(editor)) {
1569 hint = qMax(hint, editor->sizeHint().width());
1570 int min = editor->minimumSize().width();
1571 int max = editor->maximumSize().width();
1572 hint = qBound(min, hint, max);
1573 }
1574
1575 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width());
1576 }
1577
1578 return d->showGrid ? hint + 1 : hint;
1579}
1580
1581/*!
1582 Returns the y-coordinate in contents coordinates of the given \a
1583 row.
1584*/
1585int QTableView::rowViewportPosition(int row) const
1586{
1587 Q_D(const QTableView);
1588 return d->verticalHeader->sectionViewportPosition(row);
1589}
1590
1591/*!
1592 Returns the row in which the given y-coordinate, \a y, in contents
1593 coordinates is located.
1594
1595 \note This function returns -1 if the given coordinate is not valid
1596 (has no row).
1597
1598 \sa columnAt()
1599*/
1600int QTableView::rowAt(int y) const
1601{
1602 Q_D(const QTableView);
1603 return d->verticalHeader->logicalIndexAt(y);
1604}
1605
1606/*!
1607 \since 4.1
1608
1609 Sets the height of the given \a row to be \a height.
1610*/
1611void QTableView::setRowHeight(int row, int height)
1612{
1613 Q_D(const QTableView);
1614 d->verticalHeader->resizeSection(row, height);
1615}
1616
1617/*!
1618 Returns the height of the given \a row.
1619
1620 \sa resizeRowToContents(), columnWidth()
1621*/
1622int QTableView::rowHeight(int row) const
1623{
1624 Q_D(const QTableView);
1625 return d->verticalHeader->sectionSize(row);
1626}
1627
1628/*!
1629 Returns the x-coordinate in contents coordinates of the given \a
1630 column.
1631*/
1632int QTableView::columnViewportPosition(int column) const
1633{
1634 Q_D(const QTableView);
1635 return d->horizontalHeader->sectionViewportPosition(column);
1636}
1637
1638/*!
1639 Returns the column in which the given x-coordinate, \a x, in contents
1640 coordinates is located.
1641
1642 \note This function returns -1 if the given coordinate is not valid
1643 (has no column).
1644
1645 \sa rowAt()
1646*/
1647int QTableView::columnAt(int x) const
1648{
1649 Q_D(const QTableView);
1650 return d->horizontalHeader->logicalIndexAt(x);
1651}
1652
1653/*!
1654 \since 4.1
1655
1656 Sets the width of the given \a column to be \a width.
1657*/
1658void QTableView::setColumnWidth(int column, int width)
1659{
1660 Q_D(const QTableView);
1661 d->horizontalHeader->resizeSection(column, width);
1662}
1663
1664/*!
1665 Returns the width of the given \a column.
1666
1667 \sa resizeColumnToContents(), rowHeight()
1668*/
1669int QTableView::columnWidth(int column) const
1670{
1671 Q_D(const QTableView);
1672 return d->horizontalHeader->sectionSize(column);
1673}
1674
1675/*!
1676 Returns true if the given \a row is hidden; otherwise returns false.
1677
1678 \sa isColumnHidden()
1679*/
1680bool QTableView::isRowHidden(int row) const
1681{
1682 Q_D(const QTableView);
1683 return d->verticalHeader->isSectionHidden(row);
1684}
1685
1686/*!
1687 If \a hide is true \a row will be hidden, otherwise it will be shown.
1688
1689 \sa setColumnHidden()
1690*/
1691void QTableView::setRowHidden(int row, bool hide)
1692{
1693 Q_D(QTableView);
1694 if (row < 0 || row >= d->verticalHeader->count())
1695 return;
1696 d->verticalHeader->setSectionHidden(row, hide);
1697}
1698
1699/*!
1700 Returns true if the given \a column is hidden; otherwise returns false.
1701
1702 \sa isRowHidden()
1703*/
1704bool QTableView::isColumnHidden(int column) const
1705{
1706 Q_D(const QTableView);
1707 return d->horizontalHeader->isSectionHidden(column);
1708}
1709
1710/*!
1711 If \a hide is true the given \a column will be hidden; otherwise it
1712 will be shown.
1713
1714 \sa setRowHidden()
1715*/
1716void QTableView::setColumnHidden(int column, bool hide)
1717{
1718 Q_D(QTableView);
1719 if (column < 0 || column >= d->horizontalHeader->count())
1720 return;
1721 d->horizontalHeader->setSectionHidden(column, hide);
1722}
1723
1724/*!
1725 \since 4.2
1726 \property QTableView::sortingEnabled
1727 \brief whether sorting is enabled
1728
1729 If this property is true, sorting is enabled for the table; if the
1730 property is false, sorting is not enabled. The default value is false.
1731
1732 \sa sortByColumn()
1733*/
1734
1735void QTableView::setSortingEnabled(bool enable)
1736{
1737 Q_D(QTableView);
1738 d->sortingEnabled = enable;
1739 horizontalHeader()->setSortIndicatorShown(enable);
1740 if (enable) {
1741 disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
1742 this, SLOT(_q_selectColumn(int)));
1743 disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)),
1744 this, SLOT(selectColumn(int)));
1745 connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
1746 this, SLOT(sortByColumn(int)));
1747 sortByColumn(horizontalHeader()->sortIndicatorSection(),
1748 horizontalHeader()->sortIndicatorOrder());
1749 } else {
1750 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
1751 this, SLOT(_q_selectColumn(int)));
1752 connect(horizontalHeader(), SIGNAL(sectionPressed(int)),
1753 this, SLOT(selectColumn(int)));
1754 disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
1755 this, SLOT(sortByColumn(int)));
1756 }
1757}
1758
1759bool QTableView::isSortingEnabled() const
1760{
1761 Q_D(const QTableView);
1762 return d->sortingEnabled;
1763}
1764
1765/*!
1766 \property QTableView::showGrid
1767 \brief whether the grid is shown
1768
1769 If this property is true a grid is drawn for the table; if the
1770 property is false, no grid is drawn. The default value is true.
1771*/
1772bool QTableView::showGrid() const
1773{
1774 Q_D(const QTableView);
1775 return d->showGrid;
1776}
1777
1778void QTableView::setShowGrid(bool show)
1779{
1780 Q_D(QTableView);
1781 if (d->showGrid != show) {
1782 d->showGrid = show;
1783 d->viewport->update();
1784 }
1785}
1786
1787/*!
1788 \property QTableView::gridStyle
1789 \brief the pen style used to draw the grid.
1790
1791 This property holds the style used when drawing the grid (see \l{showGrid}).
1792*/
1793Qt::PenStyle QTableView::gridStyle() const
1794{
1795 Q_D(const QTableView);
1796 return d->gridStyle;
1797}
1798
1799void QTableView::setGridStyle(Qt::PenStyle style)
1800{
1801 Q_D(QTableView);
1802 if (d->gridStyle != style) {
1803 d->gridStyle = style;
1804 d->viewport->update();
1805 }
1806}
1807
1808/*!
1809 \property QTableView::wordWrap
1810 \brief the item text word-wrapping policy
1811 \since 4.3
1812
1813 If this property is true then the item text is wrapped where
1814 necessary at word-breaks; otherwise it is not wrapped at all.
1815 This property is true by default.
1816
1817 Note that even of wrapping is enabled, the cell will not be
1818 expanded to fit all text. Ellipsis will be inserted according to
1819 the current \l{QAbstractItemView::}{textElideMode}.
1820
1821*/
1822void QTableView::setWordWrap(bool on)
1823{
1824 Q_D(QTableView);
1825 if (d->wrapItemText == on)
1826 return;
1827 d->wrapItemText = on;
1828 QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
1829 QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
1830}
1831
1832bool QTableView::wordWrap() const
1833{
1834 Q_D(const QTableView);
1835 return d->wrapItemText;
1836}
1837
1838/*!
1839 \property QTableView::cornerButtonEnabled
1840 \brief whether the button in the top-left corner is enabled
1841 \since 4.3
1842
1843 If this property is true then button in the top-left corner
1844 of the table view is enabled. Clicking on this button will
1845 select all the cells in the table view.
1846
1847 This property is true by default.
1848*/
1849void QTableView::setCornerButtonEnabled(bool enable)
1850{
1851 Q_D(QTableView);
1852 d->cornerWidget->setEnabled(enable);
1853}
1854
1855bool QTableView::isCornerButtonEnabled() const
1856{
1857 Q_D(const QTableView);
1858 return d->cornerWidget->isEnabled();
1859}
1860
1861/*!
1862 \internal
1863
1864 Returns the rectangle on the viewport occupied by the given \a
1865 index.
1866 If the index is hidden in the view it will return a null QRect.
1867*/
1868QRect QTableView::visualRect(const QModelIndex &index) const
1869{
1870 Q_D(const QTableView);
1871 if (!d->isIndexValid(index) || index.parent() != d->root || isIndexHidden(index) )
1872 return QRect();
1873
1874 d->executePostedLayout();
1875
1876 if (d->hasSpans()) {
1877 QTableViewPrivate::Span span = d->span(index.row(), index.column());
1878 return d->visualSpanRect(span);
1879 }
1880
1881 int rowp = rowViewportPosition(index.row());
1882 int rowh = rowHeight(index.row());
1883 int colp = columnViewportPosition(index.column());
1884 int colw = columnWidth(index.column());
1885
1886 const int i = showGrid() ? 1 : 0;
1887 return QRect(colp, rowp, colw - i, rowh - i);
1888}
1889
1890/*!
1891 \internal
1892
1893 Makes sure that the given \a item is visible in the table view,
1894 scrolling if necessary.
1895*/
1896void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
1897{
1898 Q_D(QTableView);
1899
1900 // check if we really need to do anything
1901 if (!d->isIndexValid(index)
1902 || (d->model->parent(index) != d->root)
1903 || isIndexHidden(index))
1904 return;
1905
1906 QTableViewPrivate::Span span;
1907 if (d->hasSpans())
1908 span = d->span(index.row(), index.column());
1909
1910 // Adjust horizontal position
1911
1912 int viewportWidth = d->viewport->width();
1913 int horizontalOffset = d->horizontalHeader->offset();
1914 int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
1915 int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
1916 int cellWidth = d->hasSpans()
1917 ? d->columnSpanWidth(index.column(), span.width())
1918 : d->horizontalHeader->sectionSize(index.column());
1919
1920 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
1921
1922 bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
1923 bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
1924
1925 if (hint == PositionAtCenter || positionAtRight) {
1926 int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
1927 int x = cellWidth;
1928 while (horizontalIndex > 0) {
1929 x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
1930 if (x > w)
1931 break;
1932 --horizontalIndex;
1933 }
1934 }
1935
1936 if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
1937 int hiddenSections = 0;
1938 if (d->horizontalHeader->sectionsHidden()) {
1939 for (int s = horizontalIndex; s >= 0; --s) {
1940 int column = d->horizontalHeader->logicalIndex(s);
1941 if (d->horizontalHeader->isSectionHidden(column))
1942 ++hiddenSections;
1943 }
1944 }
1945 horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
1946 }
1947
1948 } else { // ScrollPerPixel
1949 if (hint == PositionAtCenter) {
1950 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
1951 } else {
1952 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
1953 horizontalScrollBar()->setValue(horizontalPosition);
1954 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
1955 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
1956 }
1957 }
1958
1959 // Adjust vertical position
1960
1961 int viewportHeight = d->viewport->height();
1962 int verticalOffset = d->verticalHeader->offset();
1963 int verticalPosition = d->verticalHeader->sectionPosition(index.row());
1964 int verticalIndex = d->verticalHeader->visualIndex(index.row());
1965 int cellHeight = d->hasSpans()
1966 ? d->rowSpanHeight(index.row(), span.height())
1967 : d->verticalHeader->sectionSize(index.row());
1968
1969 if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
1970 if (hint == EnsureVisible)
1971 hint = PositionAtTop;
1972 } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
1973 if (hint == EnsureVisible)
1974 hint = PositionAtBottom;
1975 }
1976
1977 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1978
1979 if (hint == PositionAtBottom || hint == PositionAtCenter) {
1980 int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
1981 int y = cellHeight;
1982 while (verticalIndex > 0) {
1983 int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
1984 y += d->verticalHeader->sectionSize(row);
1985 if (y > h)
1986 break;
1987 --verticalIndex;
1988 }
1989 }
1990
1991 if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) {
1992 int hiddenSections = 0;
1993 if (d->verticalHeader->sectionsHidden()) {
1994 for (int s = verticalIndex; s >= 0; --s) {
1995 int row = d->verticalHeader->logicalIndex(s);
1996 if (d->verticalHeader->isSectionHidden(row))
1997 ++hiddenSections;
1998 }
1999 }
2000 verticalScrollBar()->setValue(verticalIndex - hiddenSections);
2001 }
2002
2003 } else { // ScrollPerPixel
2004 if (hint == PositionAtTop) {
2005 verticalScrollBar()->setValue(verticalPosition);
2006 } else if (hint == PositionAtBottom) {
2007 verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
2008 } else if (hint == PositionAtCenter) {
2009 verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
2010 }
2011 }
2012
2013 d->setDirtyRegion(visualRect(index));
2014}
2015
2016/*!
2017 This slot is called to change the height of the given \a row. The
2018 old height is specified by \a oldHeight, and the new height by \a
2019 newHeight.
2020
2021 \sa columnResized()
2022*/
2023void QTableView::rowResized(int row, int, int)
2024{
2025 Q_D(QTableView);
2026 d->rowsToUpdate.append(row);
2027 if (d->rowResizeTimerID == 0)
2028 d->rowResizeTimerID = startTimer(0);
2029}
2030
2031/*!
2032 This slot is called to change the width of the given \a column.
2033 The old width is specified by \a oldWidth, and the new width by \a
2034 newWidth.
2035
2036 \sa rowResized()
2037*/
2038void QTableView::columnResized(int column, int, int)
2039{
2040 Q_D(QTableView);
2041 d->columnsToUpdate.append(column);
2042 if (d->columnResizeTimerID == 0)
2043 d->columnResizeTimerID = startTimer(0);
2044}
2045
2046/*!
2047 \reimp
2048 */
2049void QTableView::timerEvent(QTimerEvent *event)
2050{
2051 Q_D(QTableView);
2052
2053 if (event->timerId() == d->columnResizeTimerID) {
2054 updateGeometries();
2055 killTimer(d->columnResizeTimerID);
2056 d->columnResizeTimerID = 0;
2057
2058 QRect rect;
2059 int viewportHeight = d->viewport->height();
2060 int viewportWidth = d->viewport->width();
2061 if (d->hasSpans() && d->spansIntersectColumns(d->columnsToUpdate)) {
2062 rect = QRect(0, 0, viewportWidth, viewportHeight);
2063 } else {
2064 for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
2065 int column = d->columnsToUpdate.at(i);
2066 int x = columnViewportPosition(column);
2067 if (isRightToLeft())
2068 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
2069 else
2070 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
2071 }
2072 }
2073
2074 d->viewport->update(rect.normalized());
2075 d->columnsToUpdate.clear();
2076 }
2077
2078 if (event->timerId() == d->rowResizeTimerID) {
2079 updateGeometries();
2080 killTimer(d->rowResizeTimerID);
2081 d->rowResizeTimerID = 0;
2082
2083 int viewportHeight = d->viewport->height();
2084 int viewportWidth = d->viewport->width();
2085 int top;
2086 if (d->hasSpans() && d->spansIntersectRows(d->rowsToUpdate)) {
2087 top = 0;
2088 } else {
2089 top = viewportHeight;
2090 for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
2091 int y = rowViewportPosition(d->rowsToUpdate.at(i));
2092 top = qMin(top, y);
2093 }
2094 }
2095
2096 d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
2097 d->rowsToUpdate.clear();
2098 }
2099
2100 QAbstractItemView::timerEvent(event);
2101}
2102
2103/*!
2104 This slot is called to change the index of the given \a row in the
2105 table view. The old index is specified by \a oldIndex, and the new
2106 index by \a newIndex.
2107
2108 \sa columnMoved()
2109*/
2110void QTableView::rowMoved(int, int oldIndex, int newIndex)
2111{
2112 Q_D(QTableView);
2113
2114 updateGeometries();
2115 int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
2116 int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
2117 if (d->hasSpans() && (d->spansIntersectRow(logicalOldIndex) || d->spansIntersectRow(logicalNewIndex))) {
2118 d->viewport->update();
2119 } else {
2120 int oldTop = rowViewportPosition(logicalOldIndex);
2121 int newTop = rowViewportPosition(logicalNewIndex);
2122 int oldBottom = oldTop + rowHeight(logicalOldIndex);
2123 int newBottom = newTop + rowHeight(logicalNewIndex);
2124 int top = qMin(oldTop, newTop);
2125 int bottom = qMax(oldBottom, newBottom);
2126 int height = bottom - top;
2127 d->viewport->update(0, top, d->viewport->width(), height);
2128 }
2129}
2130
2131/*!
2132 This slot is called to change the index of the given \a column in
2133 the table view. The old index is specified by \a oldIndex, and
2134 the new index by \a newIndex.
2135
2136 \sa rowMoved()
2137*/
2138void QTableView::columnMoved(int, int oldIndex, int newIndex)
2139{
2140 Q_D(QTableView);
2141
2142 updateGeometries();
2143 int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
2144 int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
2145 if (d->hasSpans() && (d->spansIntersectColumn(logicalOldIndex) || d->spansIntersectColumn(logicalNewIndex))) {
2146 d->viewport->update();
2147 } else {
2148 int oldLeft = columnViewportPosition(logicalOldIndex);
2149 int newLeft = columnViewportPosition(logicalNewIndex);
2150 int oldRight = oldLeft + columnWidth(logicalOldIndex);
2151 int newRight = newLeft + columnWidth(logicalNewIndex);
2152 int left = qMin(oldLeft, newLeft);
2153 int right = qMax(oldRight, newRight);
2154 int width = right - left;
2155 d->viewport->update(left, 0, width, d->viewport->height());
2156 }
2157}
2158
2159/*!
2160 Selects the given \a row in the table view if the current
2161 SelectionMode and SelectionBehavior allows rows to be selected.
2162
2163 \sa selectColumn()
2164*/
2165void QTableView::selectRow(int row)
2166{
2167 Q_D(QTableView);
2168 d->selectRow(row, true);
2169}
2170
2171/*!
2172 Selects the given \a column in the table view if the current
2173 SelectionMode and SelectionBehavior allows columns to be selected.
2174
2175 \sa selectRow()
2176*/
2177void QTableView::selectColumn(int column)
2178{
2179 Q_D(QTableView);
2180 d->selectColumn(column, true);
2181}
2182
2183/*!
2184 Hide the given \a row.
2185
2186 \sa showRow() hideColumn()
2187*/
2188void QTableView::hideRow(int row)
2189{
2190 Q_D(QTableView);
2191 d->verticalHeader->hideSection(row);
2192}
2193
2194/*!
2195 Hide the given \a column.
2196
2197 \sa showColumn() hideRow()
2198*/
2199void QTableView::hideColumn(int column)
2200{
2201 Q_D(QTableView);
2202 d->horizontalHeader->hideSection(column);
2203}
2204
2205/*!
2206 Show the given \a row.
2207
2208 \sa hideRow() showColumn()
2209*/
2210void QTableView::showRow(int row)
2211{
2212 Q_D(QTableView);
2213 d->verticalHeader->showSection(row);
2214}
2215
2216/*!
2217 Show the given \a column.
2218
2219 \sa hideColumn() showRow()
2220*/
2221void QTableView::showColumn(int column)
2222{
2223 Q_D(QTableView);
2224 d->horizontalHeader->showSection(column);
2225}
2226
2227/*!
2228 Resizes the given \a row based on the size hints of the delegate
2229 used to render each item in the row.
2230*/
2231void QTableView::resizeRowToContents(int row)
2232{
2233 Q_D(QTableView);
2234 int content = sizeHintForRow(row);
2235 int header = d->verticalHeader->sectionSizeHint(row);
2236 d->verticalHeader->resizeSection(row, qMax(content, header));
2237}
2238
2239/*!
2240 Resizes all rows based on the size hints of the delegate
2241 used to render each item in the rows.
2242*/
2243void QTableView::resizeRowsToContents()
2244{
2245 Q_D(QTableView);
2246 d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
2247}
2248
2249/*!
2250 Resizes the given \a column based on the size hints of the delegate
2251 used to render each item in the column.
2252
2253 \note Only visible columns will be resized. Reimplement sizeHintForColumn()
2254 to resize hidden columns as well.
2255*/
2256void QTableView::resizeColumnToContents(int column)
2257{
2258 Q_D(QTableView);
2259 int content = sizeHintForColumn(column);
2260 int header = d->horizontalHeader->sectionSizeHint(column);
2261 d->horizontalHeader->resizeSection(column, qMax(content, header));
2262}
2263
2264/*!
2265 Resizes all columns based on the size hints of the delegate
2266 used to render each item in the columns.
2267*/
2268void QTableView::resizeColumnsToContents()
2269{
2270 Q_D(QTableView);
2271 d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
2272}
2273
2274/*!
2275 \obsolete
2276 \overload
2277
2278 Sorts the model by the values in the given \a column.
2279*/
2280void QTableView::sortByColumn(int column)
2281{
2282 Q_D(QTableView);
2283 if (column == -1)
2284 return;
2285 d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
2286}
2287
2288/*!
2289 \since 4.2
2290
2291 Sorts the model by the values in the given \a column in the given \a order.
2292
2293 \sa sortingEnabled
2294 */
2295void QTableView::sortByColumn(int column, Qt::SortOrder order)
2296{
2297 Q_D(QTableView);
2298 d->horizontalHeader->setSortIndicator(column, order);
2299 sortByColumn(column);
2300}
2301
2302/*!
2303 \internal
2304*/
2305void QTableView::verticalScrollbarAction(int action)
2306{
2307 QAbstractItemView::verticalScrollbarAction(action);
2308}
2309
2310/*!
2311 \internal
2312*/
2313void QTableView::horizontalScrollbarAction(int action)
2314{
2315 QAbstractItemView::horizontalScrollbarAction(action);
2316}
2317
2318/*!
2319 \reimp
2320*/
2321bool QTableView::isIndexHidden(const QModelIndex &index) const
2322{
2323 Q_D(const QTableView);
2324 Q_ASSERT(d->isIndexValid(index));
2325 if (isRowHidden(index.row()) || isColumnHidden(index.column()))
2326 return true;
2327 if (d->hasSpans()) {
2328 QTableViewPrivate::Span span = d->span(index.row(), index.column());
2329 return !((span.top() == index.row()) && (span.left() == index.column()));
2330 }
2331 return false;
2332}
2333
2334/*!
2335 \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
2336 \since 4.2
2337
2338 Sets the span of the table element at (\a row, \a column) to the number of
2339 rows and columns specified by (\a rowSpanCount, \a columnSpanCount).
2340
2341 \sa rowSpan(), columnSpan()
2342*/
2343void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
2344{
2345 Q_D(QTableView);
2346 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
2347 return;
2348 d->setSpan(row, column, rowSpan, columnSpan);
2349 d->viewport->update();
2350}
2351
2352/*!
2353 \since 4.2
2354
2355 Returns the row span of the table element at (\a row, \a column).
2356 The default is 1.
2357
2358 \sa setSpan(), columnSpan()
2359*/
2360int QTableView::rowSpan(int row, int column) const
2361{
2362 Q_D(const QTableView);
2363 return d->rowSpan(row, column);
2364}
2365
2366/*!
2367 \since 4.2
2368
2369 Returns the column span of the table element at (\a row, \a
2370 column). The default is 1.
2371
2372 \sa setSpan(), rowSpan()
2373*/
2374int QTableView::columnSpan(int row, int column) const
2375{
2376 Q_D(const QTableView);
2377 return d->columnSpan(row, column);
2378}
2379
2380/*!
2381 \since 4.4
2382
2383 Removes all row and column spans in the table view.
2384
2385 \sa setSpan()
2386*/
2387
2388void QTableView::clearSpans()
2389{
2390 Q_D(QTableView);
2391 d->spans.clear();
2392 d->viewport->update();
2393}
2394
2395void QTableViewPrivate::_q_selectRow(int row)
2396{
2397 selectRow(row, false);
2398}
2399
2400void QTableViewPrivate::_q_selectColumn(int column)
2401{
2402 selectColumn(column, false);
2403}
2404
2405void QTableViewPrivate::selectRow(int row, bool anchor)
2406{
2407 Q_Q(QTableView);
2408
2409 if (q->selectionBehavior() == QTableView::SelectColumns
2410 || (q->selectionMode() == QTableView::SingleSelection
2411 && q->selectionBehavior() == QTableView::SelectItems))
2412 return;
2413
2414 if (row >= 0 && row < model->rowCount(root)) {
2415 int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
2416 QModelIndex index = model->index(row, column, root);
2417 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
2418 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
2419 if ((!(command & QItemSelectionModel::Current) && anchor)
2420 || (q->selectionMode() == QTableView::SingleSelection))
2421 rowSectionAnchor = row;
2422 QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root);
2423 QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root);
2424 if (verticalHeader->sectionsMoved() && tl.row() != br.row())
2425 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
2426 else
2427 selectionModel->select(QItemSelection(tl, br), command);
2428 }
2429}
2430
2431void QTableViewPrivate::selectColumn(int column, bool anchor)
2432{
2433 Q_Q(QTableView);
2434
2435 if (q->selectionBehavior() == QTableView::SelectRows
2436 || (q->selectionMode() == QTableView::SingleSelection
2437 && q->selectionBehavior() == QTableView::SelectItems))
2438 return;
2439
2440 if (column >= 0 && column < model->columnCount(root)) {
2441 int row = verticalHeader->logicalIndexAt(0);
2442 QModelIndex index = model->index(row, column, root);
2443 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
2444 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
2445 if ((!(command & QItemSelectionModel::Current) && anchor)
2446 || (q->selectionMode() == QTableView::SingleSelection))
2447 columnSectionAnchor = column;
2448 QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root);
2449 QModelIndex br = model->index(model->rowCount(root) - 1,
2450 qMax(columnSectionAnchor, column), root);
2451 if (horizontalHeader->sectionsMoved() && tl.column() != br.column())
2452 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
2453 else
2454 selectionModel->select(QItemSelection(tl, br), command);
2455 }
2456}
2457
2458/*!
2459 \reimp
2460 */
2461void QTableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
2462{
2463#ifndef QT_NO_ACCESSIBILITY
2464 if (QAccessible::isActive()) {
2465 if (current.isValid()) {
2466 int entry = visualIndex(current) + 1;
2467 if (horizontalHeader())
2468 ++entry;
2469 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
2470 }
2471 }
2472#endif
2473 QAbstractItemView::currentChanged(current, previous);
2474}
2475
2476/*!
2477 \reimp
2478 */
2479void QTableView::selectionChanged(const QItemSelection &selected,
2480 const QItemSelection &deselected)
2481{
2482#ifndef QT_NO_ACCESSIBILITY
2483 if (QAccessible::isActive()) {
2484 // ### does not work properly for selection ranges.
2485 QModelIndex sel = selected.indexes().value(0);
2486 if (sel.isValid()) {
2487 int entry = visualIndex(sel);
2488 if (horizontalHeader())
2489 ++entry;
2490 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
2491 }
2492 QModelIndex desel = deselected.indexes().value(0);
2493 if (desel.isValid()) {
2494 int entry = visualIndex(sel);
2495 if (horizontalHeader())
2496 ++entry;
2497 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
2498 }
2499 }
2500#endif
2501 QAbstractItemView::selectionChanged(selected, deselected);
2502}
2503
2504int QTableView::visualIndex(const QModelIndex &index) const
2505{
2506 return index.row();
2507}
2508
2509QT_END_NAMESPACE
2510
2511#include "qtableview.moc"
2512
2513#include "moc_qtableview.cpp"
2514
2515#endif // QT_NO_TABLEVIEW
Note: See TracBrowser for help on using the repository browser.