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

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 105.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
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 have questions regarding the use of this file, please contact
37** Nokia 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
62/** \internal
63 Add a span to the collection. the collection takes the ownership.
64 */
65void QSpanCollection::addSpan(QSpanCollection::Span *span)
66{
67 spans.append(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.
71 SubIndex sub_index;
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);
80 }
81 }
82 it_y = index.insert(-span->top(), sub_index);
83 //we will insert span to *it_y in the later loop
84 }
85
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())
90 break;
91 --it_y;
92 }
93}
94
95
96/** \internal
97* Has to be called after the height and width of a span is changed.
98*
99* old_height is the height before the change
100*
101* if the size of the span is now 0x0 the span will be deleted.
102*/
103void QSpanCollection::updateSpan(QSpanCollection::Span *span, int old_height)
104{
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())
112 break;
113 --it_y;
114 }
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);
125 }
126 }
127 if(it_y == index.begin())
128 break;
129 --it_y;
130 }
131 }
132
133 if (span->width() == 0 && span->height() == 0) {
134 spans.removeOne(span);
135 delete span;
136 }
137}
138
139/** \internal
140 * \return a spans that spans over cell x,y (column,row) or 0 if there is none.
141 */
142QSpanCollection::Span *QSpanCollection::spanAt(int x, int y) const
143{
144 Index::const_iterator it_y = index.lowerBound(-y);
145 if (it_y == index.end())
146 return 0;
147 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
148 if (it_x == (*it_y).end())
149 return 0;
150 Span *span = *it_x;
151 if (span->right() >= x && span->bottom() >= y)
152 return span;
153 return 0;
154}
155
156
157/** \internal
158* remove and deletes all spans inside the collection
159*/
160void QSpanCollection::clear()
161{
162 qDeleteAll(spans);
163 index.clear();
164 spans.clear();
165}
166
167/** \internal
168 * return a list to all the spans that spans over cells in the given rectangle
169 */
170QList<QSpanCollection::Span *> QSpanCollection::spansInRect(int x, int y, int w, int h) const
171{
172 QSet<Span *> list;
173 Index::const_iterator it_y = index.lowerBound(-y);
174 if(it_y == index.end())
175 --it_y;
176 while(-it_y.key() <= y + h) {
177 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
178 if (it_x == (*it_y).end())
179 --it_x;
180 while(-it_x.key() <= x + w) {
181 Span *s = *it_x;
182 if (s->bottom() >= y && s->right() >= x)
183 list << s;
184 if (it_x == (*it_y).begin())
185 break;
186 --it_x;
187 }
188 if(it_y == index.begin())
189 break;
190 --it_y;
191 }
192 return list.toList();
193}
194
195#undef DEBUG_SPAN_UPDATE
196
197#ifdef DEBUG_SPAN_UPDATE
198QDebug operator<<(QDebug str, const QSpanCollection::Span &span)
199{
200 str << "(" << span.top() << "," << span.left() << "," << span.bottom() << "," << span.right() << ")";
201 return str;
202}
203#endif
204
205/** \internal
206* Updates the span collection after row insertion.
207*/
208void QSpanCollection::updateInsertedRows(int start, int end)
209{
210#ifdef DEBUG_SPAN_UPDATE
211 qDebug() << Q_FUNC_INFO;
212 qDebug() << start << end;
213 qDebug() << index;
214#endif
215 if (spans.isEmpty())
216 return;
217
218 int delta = end - start + 1;
219#ifdef DEBUG_SPAN_UPDATE
220 qDebug("Before");
221#endif
222 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
223 Span *span = *it;
224#ifdef DEBUG_SPAN_UPDATE
225 qDebug() << span << *span;
226#endif
227 if (span->m_bottom < start)
228 continue;
229 if (span->m_top >= start)
230 span->m_top += delta;
231 span->m_bottom += delta;
232 }
233
234#ifdef DEBUG_SPAN_UPDATE
235 qDebug("After");
236 foreach (QSpanCollection::Span *span, spans)
237 qDebug() << span << *span;
238#endif
239
240 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
241 int y = -it_y.key();
242 if (y < start) {
243 ++it_y;
244 continue;
245 }
246
247 index.insert(-y - delta, it_y.value());
248 it_y = index.erase(it_y);
249 }
250#ifdef DEBUG_SPAN_UPDATE
251 qDebug() << index;
252#endif
253}
254
255/** \internal
256* Updates the span collection after column insertion.
257*/
258void QSpanCollection::updateInsertedColumns(int start, int end)
259{
260#ifdef DEBUG_SPAN_UPDATE
261 qDebug() << Q_FUNC_INFO;
262 qDebug() << start << end;
263 qDebug() << index;
264#endif
265 if (spans.isEmpty())
266 return;
267
268 int delta = end - start + 1;
269#ifdef DEBUG_SPAN_UPDATE
270 qDebug("Before");
271#endif
272 for (SpanList::iterator it = spans.begin(); it != spans.end(); ++it) {
273 Span *span = *it;
274#ifdef DEBUG_SPAN_UPDATE
275 qDebug() << span << *span;
276#endif
277 if (span->m_right < start)
278 continue;
279 if (span->m_left >= start)
280 span->m_left += delta;
281 span->m_right += delta;
282 }
283
284#ifdef DEBUG_SPAN_UPDATE
285 qDebug("After");
286 foreach (QSpanCollection::Span *span, spans)
287 qDebug() << span << *span;
288#endif
289
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(); ) {
293 int x = -it.key();
294 if (x < start) {
295 ++it;
296 continue;
297 }
298 subindex.insert(-x - delta, it.value());
299 it = subindex.erase(it);
300 }
301 }
302#ifdef DEBUG_SPAN_UPDATE
303 qDebug() << index;
304#endif
305}
306
307/** \internal
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.
311*/
312bool QSpanCollection::cleanSpanSubIndex(QSpanCollection::SubIndex &subindex, int y, bool update)
313{
314 if (subindex.isEmpty())
315 return true;
316
317 bool should_be_deleted = true;
318 SubIndex::iterator it = subindex.end();
319 do {
320 --it;
321 int x = -it.key();
322 Span *span = it.value();
323 if (span->will_be_deleted) {
324 it = subindex.erase(it);
325 continue;
326 }
327 if (update && span->m_left != x) {
328 subindex.insert(-span->m_left, span);
329 it = subindex.erase(it);
330 }
331 if (should_be_deleted && span->m_top == y)
332 should_be_deleted = false;
333 } while (it != subindex.begin());
334
335 return should_be_deleted;
336}
337
338/** \internal
339* Updates the span collection after row removal.
340*/
341void QSpanCollection::updateRemovedRows(int start, int end)
342{
343#ifdef DEBUG_SPAN_UPDATE
344 qDebug() << Q_FUNC_INFO;
345 qDebug() << start << end;
346 qDebug() << index;
347#endif
348 if (spans.isEmpty())
349 return;
350
351 SpanList spansToBeDeleted;
352 int delta = end - start + 1;
353#ifdef DEBUG_SPAN_UPDATE
354 qDebug("Before");
355#endif
356 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
357 Span *span = *it;
358#ifdef DEBUG_SPAN_UPDATE
359 qDebug() << span << *span;
360#endif
361 if (span->m_bottom < start) {
362 ++it;
363 continue;
364 }
365 if (span->m_top < start) {
366 if (span->m_bottom <= end)
367 span->m_bottom = start - 1;
368 else
369 span->m_bottom -= delta;
370 } else {
371 if (span->m_bottom > end) {
372 if (span->m_top <= end)
373 span->m_top = start;
374 else
375 span->m_top -= delta;
376 span->m_bottom -= delta;
377 } else {
378 span->will_be_deleted = true;
379 }
380 }
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);
386 } else {
387 ++it;
388 }
389 }
390
391#ifdef DEBUG_SPAN_UPDATE
392 qDebug("After");
393 foreach (QSpanCollection::Span *span, spans)
394 qDebug() << span << *span;
395#endif
396 if (spans.isEmpty()) {
397 qDeleteAll(spansToBeDeleted);
398 index.clear();
399 return;
400 }
401
402 Index::iterator it_y = index.end();
403 do {
404 --it_y;
405 int y = -it_y.key();
406 SubIndex &subindex = it_y.value();
407 if (y < start) {
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)
416 continue;
417 if (!span_at_start && span->m_top == start)
418 span_at_start = true;
419 spansToBeMoved.insert(it.key(), span);
420 }
421
422 if (y == start && span_at_start)
423 subindex.clear();
424 else
425 it_y = index.erase(it_y);
426
427 if (span_at_start) {
428 Index::iterator it_start;
429 if (y == start)
430 it_start = it_y;
431 else {
432 it_start = index.find(-start);
433 if (it_start == index.end())
434 it_start = index.insert(-start, SubIndex());
435 }
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());
439 }
440 } else {
441 if (y == end + 1) {
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);
449 ++it;
450 }
451 } else {
452 index.insert(-y + delta, subindex);
453 }
454 it_y = index.erase(it_y);
455 }
456 } while (it_y != index.begin());
457
458#ifdef DEBUG_SPAN_UPDATE
459 qDebug() << index;
460 qDebug("Deleted");
461 foreach (QSpanCollection::Span *span, spansToBeDeleted)
462 qDebug() << span << *span;
463#endif
464 qDeleteAll(spansToBeDeleted);
465}
466
467/** \internal
468* Updates the span collection after column removal.
469*/
470void QSpanCollection::updateRemovedColumns(int start, int end)
471{
472#ifdef DEBUG_SPAN_UPDATE
473 qDebug() << Q_FUNC_INFO;
474 qDebug() << start << end;
475 qDebug() << index;
476#endif
477 if (spans.isEmpty())
478 return;
479
480 SpanList toBeDeleted;
481 int delta = end - start + 1;
482#ifdef DEBUG_SPAN_UPDATE
483 qDebug("Before");
484#endif
485 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
486 Span *span = *it;
487#ifdef DEBUG_SPAN_UPDATE
488 qDebug() << span << *span;
489#endif
490 if (span->m_right < start) {
491 ++it;
492 continue;
493 }
494 if (span->m_left < start) {
495 if (span->m_right <= end)
496 span->m_right = start - 1;
497 else
498 span->m_right -= delta;
499 } else {
500 if (span->m_right > end) {
501 if (span->m_left <= end)
502 span->m_left = start;
503 else
504 span->m_left -= delta;
505 span->m_right -= delta;
506 } else {
507 span->will_be_deleted = true;
508 }
509 }
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);
515 } else {
516 ++it;
517 }
518 }
519
520#ifdef DEBUG_SPAN_UPDATE
521 qDebug("After");
522 foreach (QSpanCollection::Span *span, spans)
523 qDebug() << span << *span;
524#endif
525 if (spans.isEmpty()) {
526 qDeleteAll(toBeDeleted);
527 index.clear();
528 return;
529 }
530
531 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
532 int y = -it_y.key();
533 if (cleanSpanSubIndex(it_y.value(), y, true))
534 it_y = index.erase(it_y);
535 else
536 ++it_y;
537 }
538
539#ifdef DEBUG_SPAN_UPDATE
540 qDebug() << index;
541 qDebug("Deleted");
542 foreach (QSpanCollection::Span *span, toBeDeleted)
543 qDebug() << span << *span;
544#endif
545 qDeleteAll(toBeDeleted);
546}
547
548#ifdef QT_BUILD_INTERNAL
549/*!
550 \internal
551 Checks whether the span index structure is self-consistent, and consistent with the spans list.
552*/
553bool QSpanCollection::checkConsistency() const
554{
555 for (Index::const_iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
556 int y = -it_y.key();
557 const SubIndex &subIndex = it_y.value();
558 for (SubIndex::const_iterator it = subIndex.begin(); it != subIndex.end(); ++it) {
559 int x = -it.key();
560 Span *span = it.value();
561 if (!spans.contains(span) || span->left() != x
562 || y < span->top() || y > span->bottom())
563 return false;
564 }
565 }
566
567 foreach (const Span *span, spans) {
568 if (span->width() < 1 || span->height() < 1
569 || (span->width() == 1 && span->height() == 1))
570 return false;
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())
575 return false;
576 else
577 continue;
578 }
579 const SubIndex &subIndex = it_y.value();
580 SubIndex::const_iterator it = subIndex.find(-span->left());
581 if (it == subIndex.end() || it.value() != span)
582 return false;
583 }
584 }
585 return true;
586}
587#endif
588
589class QTableCornerButton : public QAbstractButton
590{
591 Q_OBJECT
592public:
593 QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
594 void paintEvent(QPaintEvent*) {
595 QStyleOptionHeader opt;
596 opt.init(this);
597 QStyle::State state = QStyle::State_None;
598 if (isEnabled())
599 state |= QStyle::State_Enabled;
600 if (isActiveWindow())
601 state |= QStyle::State_Active;
602 if (isDown())
603 state |= QStyle::State_Sunken;
604 opt.state = state;
605 opt.rect = rect();
606 opt.position = QStyleOptionHeader::OnlyOneSection;
607 QPainter painter(this);
608 style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
609 }
610};
611
612void QTableViewPrivate::init()
613{
614 Q_Q(QTableView);
615
616 q->setEditTriggers(editTriggers|QAbstractItemView::AnyKeyPressed);
617
618 QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
619 vertical->setClickable(true);
620 vertical->setHighlightSections(true);
621 q->setVerticalHeader(vertical);
622
623 QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
624 horizontal->setClickable(true);
625 horizontal->setHighlightSections(true);
626 q->setHorizontalHeader(horizontal);
627
628 tabKeyNavigation = true;
629
630 cornerWidget = new QTableCornerButton(q);
631 cornerWidget->setFocusPolicy(Qt::NoFocus);
632 QObject::connect(cornerWidget, SIGNAL(clicked()), q, SLOT(selectAll()));
633}
634
635/*!
636 \internal
637 Trims away indices that are hidden in the treeview due to hidden horizontal or vertical sections.
638*/
639void QTableViewPrivate::trimHiddenSelections(QItemSelectionRange *range) const
640{
641 Q_ASSERT(range && range->isValid());
642
643 int top = range->top();
644 int left = range->left();
645 int bottom = range->bottom();
646 int right = range->right();
647
648 while (bottom >= top && verticalHeader->isSectionHidden(bottom))
649 --bottom;
650 while (right >= left && horizontalHeader->isSectionHidden(right))
651 --right;
652
653 if (top > bottom || left > right) { // everything is hidden
654 *range = QItemSelectionRange();
655 return;
656 }
657
658 while (verticalHeader->isSectionHidden(top) && top <= bottom)
659 ++top;
660 while (horizontalHeader->isSectionHidden(left) && left <= right)
661 ++left;
662
663 if (top > bottom || left > right) { // everything is hidden
664 *range = QItemSelectionRange();
665 return;
666 }
667
668 QModelIndex bottomRight = model->index(bottom, right, range->parent());
669 QModelIndex topLeft = model->index(top, left, range->parent());
670 *range = QItemSelectionRange(topLeft, bottomRight);
671}
672
673/*!
674 \internal
675 Sets the span for the cell at (\a row, \a column).
676*/
677void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
678{
679 if (row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0) {
680 qWarning() << "QTableView::setSpan: invalid span given: (" << row << ',' << column << ',' << rowSpan << ',' << columnSpan << ')';
681 return;
682 }
683 QSpanCollection::Span *sp = spans.spanAt(column, row);
684 if (sp) {
685 if (sp->top() != row || sp->left() != column) {
686 qWarning() << "QTableView::setSpan: span cannot overlap";
687 return;
688 }
689 if (rowSpan == 1 && columnSpan == 1) {
690 rowSpan = columnSpan = 0;
691 }
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);
696 return;
697 } else if (rowSpan == 1 && columnSpan == 1) {
698 qWarning() << "QTableView::setSpan: single cell span won't be added";
699 return;
700 }
701 sp = new QSpanCollection::Span(row, column, rowSpan, columnSpan);
702 spans.addSpan(sp);
703}
704
705/*!
706 \internal
707 Gets the span information for the cell at (\a row, \a column).
708*/
709QSpanCollection::Span QTableViewPrivate::span(int row, int column) const
710{
711 QSpanCollection::Span *sp = spans.spanAt(column, row);
712 if (sp)
713 return *sp;
714
715 return QSpanCollection::Span(row, column, 1, 1);
716}
717
718/*!
719 \internal
720 Returns the logical index of the last section that's part of the span.
721*/
722int QTableViewPrivate::sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
723{
724 int visual = header->visualIndex(logical);
725 for (int i = 1; i < span; ) {
726 if (++visual >= header->count())
727 break;
728 logical = header->logicalIndex(visual);
729 ++i;
730 }
731 return logical;
732}
733
734/*!
735 \internal
736 Returns the size of the span starting at logical index \a logical
737 and spanning \a span sections.
738*/
739int QTableViewPrivate::sectionSpanSize(const QHeaderView *header, int logical, int span) const
740{
741 int endLogical = sectionSpanEndLogical(header, logical, span);
742 return header->sectionPosition(endLogical)
743 - header->sectionPosition(logical)
744 + header->sectionSize(endLogical);
745}
746
747/*!
748 \internal
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.
752*/
753bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
754{
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())
760 break;
761 spanLogical = header->logicalIndex(visual);
762 if (logical == spanLogical)
763 return true;
764 ++i;
765 }
766 return false;
767}
768
769/*!
770 \internal
771 Returns the visual rect for the given \a span.
772*/
773QRect QTableViewPrivate::visualSpanRect(const QSpanCollection::Span &span) const
774{
775 Q_Q(const QTableView);
776 // vertical
777 int row = span.top();
778 int rowp = verticalHeader->sectionViewportPosition(row);
779 int rowh = rowSpanHeight(row, span.height());
780 // horizontal
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);
786
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);
791}
792
793/*!
794 \internal
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
798*/
799void QTableViewPrivate::drawAndClipSpans(const QRegion &area, QPainter *painter,
800 const QStyleOptionViewItemV4 &option, QBitArray *drawn,
801 int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
802{
803 bool alternateBase = false;
804 QRegion region = viewport->rect();
805
806 QList<QSpanCollection::Span *> visibleSpans;
807 bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved();
808
809 if (!sectionMoved) {
810 visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow),
811 lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1);
812 } else {
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));
817 set.remove(0);
818 visibleSpans = set.toList();
819 }
820
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())
826 continue;
827 QRect rect = visualSpanRect(*span);
828 rect.translate(scrollDelayOffset);
829 if (!area.intersects(rect))
830 continue;
831 QStyleOptionViewItemV4 opt = option;
832 opt.rect = rect;
833 alternateBase = alternatingColors && (span->top() & 1);
834 if (alternateBase)
835 opt.features |= QStyleOptionViewItemV2::Alternate;
836 else
837 opt.features &= ~QStyleOptionViewItemV2::Alternate;
838 drawCell(painter, opt, index);
839 region -= rect;
840 for (int r = span->top(); r <= span->bottom(); ++r) {
841 const int vr = visualRow(r);
842 if (vr < firstVisualRow || vr > lastVisualRow)
843 continue;
844 for (int c = span->left(); c <= span->right(); ++c) {
845 const int vc = visualColumn(c);
846 if (vc < firstVisualColumn || vc > lastVisualColumn)
847 continue;
848 drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
849 + vc - firstVisualColumn);
850 }
851 }
852
853 }
854 painter->setClipRegion(region);
855}
856
857/*!
858 \internal
859 Updates spans after row insertion.
860*/
861void QTableViewPrivate::_q_updateSpanInsertedRows(const QModelIndex &parent, int start, int end)
862{
863 Q_UNUSED(parent)
864 spans.updateInsertedRows(start, end);
865}
866
867/*!
868 \internal
869 Updates spans after column insertion.
870*/
871void QTableViewPrivate::_q_updateSpanInsertedColumns(const QModelIndex &parent, int start, int end)
872{
873 Q_UNUSED(parent)
874 spans.updateInsertedColumns(start, end);
875}
876
877/*!
878 \internal
879 Updates spans after row removal.
880*/
881void QTableViewPrivate::_q_updateSpanRemovedRows(const QModelIndex &parent, int start, int end)
882{
883 Q_UNUSED(parent)
884 spans.updateRemovedRows(start, end);
885}
886
887/*!
888 \internal
889 Updates spans after column removal.
890*/
891void QTableViewPrivate::_q_updateSpanRemovedColumns(const QModelIndex &parent, int start, int end)
892{
893 Q_UNUSED(parent)
894 spans.updateRemovedColumns(start, end);
895}
896
897/*!
898 \internal
899 Draws a table cell.
900*/
901void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItemV4 &option, const QModelIndex &index)
902{
903 Q_Q(QTableView);
904 QStyleOptionViewItemV4 opt = option;
905
906 if (selectionModel && selectionModel->isSelected(index))
907 opt.state |= QStyle::State_Selected;
908 if (index == hover)
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;
915 } else {
916 cg = QPalette::Normal;
917 }
918 opt.palette.setCurrentColorGroup(cg);
919 }
920
921 if (index == q->currentIndex()) {
922 const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
923 if (focus)
924 opt.state |= QStyle::State_HasFocus;
925 }
926
927 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);
928
929 if (const QWidget *widget = editorForIndex(index).editor) {
930 painter->save();
931 painter->setClipRect(widget->geometry());
932 q->itemDelegate(index)->paint(painter, opt, index);
933 painter->restore();
934 } else {
935 q->itemDelegate(index)->paint(painter, opt, index);
936 }
937}
938
939/*!
940 \class QTableView
941
942 \brief The QTableView class provides a default model/view
943 implementation of a table view.
944
945 \ingroup model-view
946 \ingroup advanced
947
948
949 A QTableView implements a table view that displays items from a
950 model. This class is used to provide standard tables that were
951 previously provided by the QTable class, but using the more
952 flexible approach provided by Qt's model/view architecture.
953
954 The QTableView class is one of the \l{Model/View Classes}
955 and is part of Qt's \l{Model/View Programming}{model/view framework}.
956
957 QTableView implements the interfaces defined by the
958 QAbstractItemView class to allow it to display data provided by
959 models derived from the QAbstractItemModel class.
960
961 \section1 Navigation
962
963 You can navigate the cells in the table by clicking on a cell with the
964 mouse, or by using the arrow keys. Because QTableView enables
965 \l{QAbstractItemView::tabKeyNavigation}{tabKeyNavigation} by default, you
966 can also hit Tab and Backtab to move from cell to cell.
967
968 \section1 Visual Appearance
969
970 The table has a vertical header that can be obtained using the
971 verticalHeader() function, and a horizontal header that is available
972 through the horizontalHeader() function. The height of each row in the
973 table can be found by using rowHeight(); similarly, the width of
974 columns can be found using columnWidth(). Since both of these are plain
975 widgets, you can hide either of them using their hide() functions.
976
977 Rows and columns can be hidden and shown with hideRow(), hideColumn(),
978 showRow(), and showColumn(). They can be selected with selectRow()
979 and selectColumn(). The table will show a grid depending on the
980 \l showGrid property.
981
982 The items shown in a table view, like those in the other item views, are
983 rendered and edited using standard \l{QItemDelegate}{delegates}. However,
984 for some tasks it is sometimes useful to be able to insert widgets in a
985 table instead. Widgets are set for particular indexes with the
986 \l{QAbstractItemView::}{setIndexWidget()} function, and
987 later retrieved with \l{QAbstractItemView::}{indexWidget()}.
988
989 \table
990 \row \o \inlineimage qtableview-resized.png
991 \o By default, the cells in a table do not expand to fill the available space.
992
993 You can make the cells fill the available space by stretching the last
994 header section. Access the relevant header using horizontalHeader()
995 or verticalHeader() and set the header's \l{QHeaderView::}{stretchLastSection}
996 property.
997
998 To distribute the available space according to the space requirement of
999 each column or row, call the view's resizeColumnsToContents() or
1000 resizeRowsToContents() functions.
1001 \endtable
1002
1003 \section1 Coordinate Systems
1004
1005 For some specialized forms of tables it is useful to be able to
1006 convert between row and column indexes and widget coordinates.
1007 The rowAt() function provides the y-coordinate within the view of the
1008 specified row; the row index can be used to obtain a corresponding
1009 y-coordinate with rowViewportPosition(). The columnAt() and
1010 columnViewportPosition() functions provide the equivalent conversion
1011 operations between x-coordinates and column indexes.
1012
1013 \section1 Styles
1014
1015 QTableView is styled appropriately for each platform. The following images show
1016 how it looks on three different platforms. Go to the \l{Qt Widget Gallery} to see
1017 its appearance in other styles.
1018
1019 \table 100%
1020 \row \o \inlineimage windowsxp-tableview.png Screenshot of a Windows XP style table view
1021 \o \inlineimage macintosh-tableview.png Screenshot of a Macintosh style table view
1022 \o \inlineimage plastique-tableview.png Screenshot of a Plastique style table view
1023 \row \o A \l{Windows XP Style Widget Gallery}{Windows XP style} table view.
1024 \o A \l{Macintosh Style Widget Gallery}{Macintosh style} table view.
1025 \o A \l{Plastique Style Widget Gallery}{Plastique style} table view.
1026 \endtable
1027
1028 \sa QTableWidget, {View Classes}, QAbstractItemModel, QAbstractItemView,
1029 {Chart Example}, {Pixelator Example}, {Table Model Example}
1030*/
1031
1032/*!
1033 Constructs a table view with a \a parent to represent the data.
1034
1035 \sa QAbstractItemModel
1036*/
1037
1038QTableView::QTableView(QWidget *parent)
1039 : QAbstractItemView(*new QTableViewPrivate, parent)
1040{
1041 Q_D(QTableView);
1042 d->init();
1043}
1044
1045/*!
1046 \internal
1047*/
1048QTableView::QTableView(QTableViewPrivate &dd, QWidget *parent)
1049 : QAbstractItemView(dd, parent)
1050{
1051 Q_D(QTableView);
1052 d->init();
1053}
1054
1055/*!
1056 Destroys the table view.
1057*/
1058QTableView::~QTableView()
1059{
1060}
1061
1062/*!
1063 \reimp
1064*/
1065void QTableView::setModel(QAbstractItemModel *model)
1066{
1067 Q_D(QTableView);
1068 if (model == d->model)
1069 return;
1070 //let's disconnect from the old model
1071 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
1072 disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1073 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1074 disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1075 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1076 disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1077 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1078 disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1079 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1080 }
1081 if (model) { //and connect to the new one
1082 connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
1083 this, SLOT(_q_updateSpanInsertedRows(QModelIndex,int,int)));
1084 connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
1085 this, SLOT(_q_updateSpanInsertedColumns(QModelIndex,int,int)));
1086 connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
1087 this, SLOT(_q_updateSpanRemovedRows(QModelIndex,int,int)));
1088 connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
1089 this, SLOT(_q_updateSpanRemovedColumns(QModelIndex,int,int)));
1090 }
1091 d->verticalHeader->setModel(model);
1092 d->horizontalHeader->setModel(model);
1093 QAbstractItemView::setModel(model);
1094}
1095
1096/*!
1097 \reimp
1098*/
1099void QTableView::setRootIndex(const QModelIndex &index)
1100{
1101 Q_D(QTableView);
1102 if (index == d->root) {
1103 viewport()->update();
1104 return;
1105 }
1106 d->verticalHeader->setRootIndex(index);
1107 d->horizontalHeader->setRootIndex(index);
1108 QAbstractItemView::setRootIndex(index);
1109}
1110
1111/*!
1112 \reimp
1113*/
1114void QTableView::setSelectionModel(QItemSelectionModel *selectionModel)
1115{
1116 Q_D(QTableView);
1117 Q_ASSERT(selectionModel);
1118 d->verticalHeader->setSelectionModel(selectionModel);
1119 d->horizontalHeader->setSelectionModel(selectionModel);
1120 QAbstractItemView::setSelectionModel(selectionModel);
1121}
1122
1123/*!
1124 Returns the table view's horizontal header.
1125
1126 \sa setHorizontalHeader(), verticalHeader(), QAbstractItemModel::headerData()
1127*/
1128QHeaderView *QTableView::horizontalHeader() const
1129{
1130 Q_D(const QTableView);
1131 return d->horizontalHeader;
1132}
1133
1134/*!
1135 Returns the table view's vertical header.
1136
1137 \sa setVerticalHeader(), horizontalHeader(), QAbstractItemModel::headerData()
1138*/
1139QHeaderView *QTableView::verticalHeader() const
1140{
1141 Q_D(const QTableView);
1142 return d->verticalHeader;
1143}
1144
1145/*!
1146 Sets the widget to use for the horizontal header to \a header.
1147
1148 \sa horizontalHeader() setVerticalHeader()
1149*/
1150void QTableView::setHorizontalHeader(QHeaderView *header)
1151{
1152 Q_D(QTableView);
1153
1154 if (!header || header == d->horizontalHeader)
1155 return;
1156 if (d->horizontalHeader && d->horizontalHeader->parent() == this)
1157 delete d->horizontalHeader;
1158 d->horizontalHeader = header;
1159 d->horizontalHeader->setParent(this);
1160 if (!d->horizontalHeader->model()) {
1161 d->horizontalHeader->setModel(d->model);
1162 if (d->selectionModel)
1163 d->horizontalHeader->setSelectionModel(d->selectionModel);
1164 }
1165
1166 connect(d->horizontalHeader,SIGNAL(sectionResized(int,int,int)),
1167 this, SLOT(columnResized(int,int,int)));
1168 connect(d->horizontalHeader, SIGNAL(sectionMoved(int,int,int)),
1169 this, SLOT(columnMoved(int,int,int)));
1170 connect(d->horizontalHeader, SIGNAL(sectionCountChanged(int,int)),
1171 this, SLOT(columnCountChanged(int,int)));
1172 connect(d->horizontalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectColumn(int)));
1173 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectColumn(int)));
1174 connect(d->horizontalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1175 this, SLOT(resizeColumnToContents(int)));
1176 connect(d->horizontalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1177
1178 //update the sorting enabled states on the new header
1179 setSortingEnabled(d->sortingEnabled);
1180}
1181
1182/*!
1183 Sets the widget to use for the vertical header to \a header.
1184
1185 \sa verticalHeader() setHorizontalHeader()
1186*/
1187void QTableView::setVerticalHeader(QHeaderView *header)
1188{
1189 Q_D(QTableView);
1190
1191 if (!header || header == d->verticalHeader)
1192 return;
1193 if (d->verticalHeader && d->verticalHeader->parent() == this)
1194 delete d->verticalHeader;
1195 d->verticalHeader = header;
1196 d->verticalHeader->setParent(this);
1197 if (!d->verticalHeader->model()) {
1198 d->verticalHeader->setModel(d->model);
1199 if (d->selectionModel)
1200 d->verticalHeader->setSelectionModel(d->selectionModel);
1201 }
1202
1203 connect(d->verticalHeader, SIGNAL(sectionResized(int,int,int)),
1204 this, SLOT(rowResized(int,int,int)));
1205 connect(d->verticalHeader, SIGNAL(sectionMoved(int,int,int)),
1206 this, SLOT(rowMoved(int,int,int)));
1207 connect(d->verticalHeader, SIGNAL(sectionCountChanged(int,int)),
1208 this, SLOT(rowCountChanged(int,int)));
1209 connect(d->verticalHeader, SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
1210 connect(d->verticalHeader, SIGNAL(sectionEntered(int)), this, SLOT(_q_selectRow(int)));
1211 connect(d->verticalHeader, SIGNAL(sectionHandleDoubleClicked(int)),
1212 this, SLOT(resizeRowToContents(int)));
1213 connect(d->verticalHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries()));
1214}
1215
1216/*!
1217 \internal
1218
1219 Scroll the contents of the table view by (\a dx, \a dy).
1220*/
1221void QTableView::scrollContentsBy(int dx, int dy)
1222{
1223 Q_D(QTableView);
1224
1225 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
1226
1227 dx = isRightToLeft() ? -dx : dx;
1228 if (dx) {
1229 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
1230 int oldOffset = d->horizontalHeader->offset();
1231 if (horizontalScrollBar()->value() == horizontalScrollBar()->maximum())
1232 d->horizontalHeader->setOffsetToLastSection();
1233 else
1234 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
1235 int newOffset = d->horizontalHeader->offset();
1236 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
1237 } else {
1238 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
1239 }
1240 }
1241 if (dy) {
1242 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
1243 int oldOffset = d->verticalHeader->offset();
1244 if (verticalScrollBar()->value() == verticalScrollBar()->maximum())
1245 d->verticalHeader->setOffsetToLastSection();
1246 else
1247 d->verticalHeader->setOffsetToSectionPosition(verticalScrollBar()->value());
1248 int newOffset = d->verticalHeader->offset();
1249 dy = oldOffset - newOffset;
1250 } else {
1251 d->verticalHeader->setOffset(verticalScrollBar()->value());
1252 }
1253 }
1254 d->scrollContentsBy(dx, dy);
1255
1256 if (d->showGrid) {
1257 //we need to update the first line of the previous top item in the view
1258 //because it has the grid drawn if the header is invisible.
1259 //It is strictly related to what's done at then end of the paintEvent
1260 if (dy > 0 && d->horizontalHeader->isHidden() && d->verticalScrollMode == ScrollPerItem) {
1261 d->viewport->update(0, dy, d->viewport->width(), dy);
1262 }
1263 if (dx > 0 && d->verticalHeader->isHidden() && d->horizontalScrollMode == ScrollPerItem) {
1264 d->viewport->update(dx, 0, dx, d->viewport->height());
1265 }
1266 }
1267}
1268
1269/*!
1270 \reimp
1271*/
1272QStyleOptionViewItem QTableView::viewOptions() const
1273{
1274 QStyleOptionViewItem option = QAbstractItemView::viewOptions();
1275 option.showDecorationSelected = true;
1276 return option;
1277}
1278
1279/*!
1280 Paints the table on receipt of the given paint event \a event.
1281*/
1282void QTableView::paintEvent(QPaintEvent *event)
1283{
1284 Q_D(QTableView);
1285 // setup temp variables for the painting
1286 QStyleOptionViewItemV4 option = d->viewOptionsV4();
1287 const QPoint offset = d->scrollDelayOffset;
1288 const bool showGrid = d->showGrid;
1289 const int gridSize = showGrid ? 1 : 0;
1290 const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
1291 const QColor gridColor = static_cast<QRgb>(gridHint);
1292 const QPen gridPen = QPen(gridColor, 0, d->gridStyle);
1293 const QHeaderView *verticalHeader = d->verticalHeader;
1294 const QHeaderView *horizontalHeader = d->horizontalHeader;
1295 const QStyle::State state = option.state;
1296 const bool alternate = d->alternatingColors;
1297 const bool rightToLeft = isRightToLeft();
1298
1299 QPainter painter(d->viewport);
1300
1301 // if there's nothing to do, clear the area and return
1302 if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
1303 return;
1304
1305 uint x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
1306 uint y = verticalHeader->length() - verticalHeader->offset() - 1;
1307
1308 const QRegion region = event->region().translated(offset);
1309 const QVector<QRect> rects = region.rects();
1310
1311 //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
1312 //same goes for ...VisualColumn
1313 int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
1314 int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->viewport()->height());
1315 if (lastVisualRow == -1)
1316 lastVisualRow = d->model->rowCount(d->root) - 1;
1317
1318 int firstVisualColumn = horizontalHeader->visualIndexAt(0);
1319 int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->viewport()->width());
1320 if (rightToLeft)
1321 qSwap(firstVisualColumn, lastVisualColumn);
1322 if (firstVisualColumn == -1)
1323 firstVisualColumn = 0;
1324 if (lastVisualColumn == -1)
1325 lastVisualColumn = horizontalHeader->count() - 1;
1326
1327 QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
1328
1329 if (d->hasSpans()) {
1330 d->drawAndClipSpans(region, &painter, option, &drawn,
1331 firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
1332 }
1333
1334 for (int i = 0; i < rects.size(); ++i) {
1335 QRect dirtyArea = rects.at(i);
1336 dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
1337 if (rightToLeft) {
1338 dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
1339 } else {
1340 dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
1341 }
1342
1343 // get the horizontal start and end visual sections
1344 int left = horizontalHeader->visualIndexAt(dirtyArea.left());
1345 int right = horizontalHeader->visualIndexAt(dirtyArea.right());
1346 if (rightToLeft)
1347 qSwap(left, right);
1348 if (left == -1) left = 0;
1349 if (right == -1) right = horizontalHeader->count() - 1;
1350
1351 // get the vertical start and end visual sections and if alternate color
1352 int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
1353 if (bottom == -1) bottom = verticalHeader->count() - 1;
1354 int top = 0;
1355 bool alternateBase = false;
1356 if (alternate && verticalHeader->sectionsHidden()) {
1357 uint verticalOffset = verticalHeader->offset();
1358 int row = verticalHeader->logicalIndex(top);
1359 for (int y = 0;
1360 ((uint)(y += verticalHeader->sectionSize(top)) <= verticalOffset) && (top < bottom);
1361 ++top) {
1362 row = verticalHeader->logicalIndex(top);
1363 if (alternate && !verticalHeader->isSectionHidden(row))
1364 alternateBase = !alternateBase;
1365 }
1366 } else {
1367 top = verticalHeader->visualIndexAt(dirtyArea.top());
1368 alternateBase = (top & 1) && alternate;
1369 }
1370 if (top == -1 || top > bottom)
1371 continue;
1372
1373 // Paint each row item
1374 for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
1375 int row = verticalHeader->logicalIndex(visualRowIndex);
1376 if (verticalHeader->isSectionHidden(row))
1377 continue;
1378 int rowY = rowViewportPosition(row);
1379 rowY += offset.y();
1380 int rowh = rowHeight(row) - gridSize;
1381
1382 // Paint each column item
1383 for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
1384 int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
1385 + visualColumnIndex - firstVisualColumn;
1386
1387 if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
1388 continue;
1389 drawn.setBit(currentBit);
1390
1391 int col = horizontalHeader->logicalIndex(visualColumnIndex);
1392 if (horizontalHeader->isSectionHidden(col))
1393 continue;
1394 int colp = columnViewportPosition(col);
1395 colp += offset.x();
1396 int colw = columnWidth(col) - gridSize;
1397
1398 const QModelIndex index = d->model->index(row, col, d->root);
1399 if (index.isValid()) {
1400 option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
1401 if (alternate) {
1402 if (alternateBase)
1403 option.features |= QStyleOptionViewItemV2::Alternate;
1404 else
1405 option.features &= ~QStyleOptionViewItemV2::Alternate;
1406 }
1407 d->drawCell(&painter, option, index);
1408 }
1409 }
1410 alternateBase = !alternateBase && alternate;
1411 }
1412
1413 if (showGrid) {
1414 // Find the bottom right (the last rows/columns might be hidden)
1415 while (verticalHeader->isSectionHidden(verticalHeader->logicalIndex(bottom))) --bottom;
1416 QPen old = painter.pen();
1417 painter.setPen(gridPen);
1418 // Paint each row
1419 for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
1420 int row = verticalHeader->logicalIndex(visualIndex);
1421 if (verticalHeader->isSectionHidden(row))
1422 continue;
1423 int rowY = rowViewportPosition(row);
1424 rowY += offset.y();
1425 int rowh = rowHeight(row) - gridSize;
1426 painter.drawLine(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
1427 }
1428
1429 // Paint each column
1430 for (int h = left; h <= right; ++h) {
1431 int col = horizontalHeader->logicalIndex(h);
1432 if (horizontalHeader->isSectionHidden(col))
1433 continue;
1434 int colp = columnViewportPosition(col);
1435 colp += offset.x();
1436 if (!rightToLeft)
1437 colp += columnWidth(col) - gridSize;
1438 painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
1439 }
1440
1441 //draw the top & left grid lines if the headers are not visible.
1442 //We do update this line when subsequent scroll happen (see scrollContentsBy)
1443 if (horizontalHeader->isHidden() && verticalScrollMode() == ScrollPerItem)
1444 painter.drawLine(dirtyArea.left(), 0, dirtyArea.right(), 0);
1445 if (verticalHeader->isHidden() && horizontalScrollMode() == ScrollPerItem)
1446 painter.drawLine(0, dirtyArea.top(), 0, dirtyArea.bottom());
1447 painter.setPen(old);
1448 }
1449 }
1450
1451#ifndef QT_NO_DRAGANDDROP
1452 // Paint the dropIndicator
1453 d->paintDropIndicator(&painter);
1454#endif
1455}
1456
1457/*!
1458 Returns the index position of the model item corresponding to the
1459 table item at position \a pos in contents coordinates.
1460*/
1461QModelIndex QTableView::indexAt(const QPoint &pos) const
1462{
1463 Q_D(const QTableView);
1464 d->executePostedLayout();
1465 int r = rowAt(pos.y());
1466 int c = columnAt(pos.x());
1467 if (r >= 0 && c >= 0) {
1468 if (d->hasSpans()) {
1469 QSpanCollection::Span span = d->span(r, c);
1470 r = span.top();
1471 c = span.left();
1472 }
1473 return d->model->index(r, c, d->root);
1474 }
1475 return QModelIndex();
1476}
1477
1478/*!
1479 Returns the horizontal offset of the items in the table view.
1480
1481 Note that the table view uses the horizontal header section
1482 positions to determine the positions of columns in the view.
1483
1484 \sa verticalOffset()
1485*/
1486int QTableView::horizontalOffset() const
1487{
1488 Q_D(const QTableView);
1489 return d->horizontalHeader->offset();
1490}
1491
1492/*!
1493 Returns the vertical offset of the items in the table view.
1494
1495 Note that the table view uses the vertical header section
1496 positions to determine the positions of rows in the view.
1497
1498 \sa horizontalOffset()
1499*/
1500int QTableView::verticalOffset() const
1501{
1502 Q_D(const QTableView);
1503 return d->verticalHeader->offset();
1504}
1505
1506/*!
1507 \fn QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1508
1509 Moves the cursor in accordance with the given \a cursorAction, using the
1510 information provided by the \a modifiers.
1511
1512 \sa QAbstractItemView::CursorAction
1513*/
1514QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1515{
1516 Q_D(QTableView);
1517 Q_UNUSED(modifiers);
1518
1519 int bottom = d->model->rowCount(d->root) - 1;
1520 // make sure that bottom is the bottommost *visible* row
1521 while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
1522 --bottom;
1523
1524 int right = d->model->columnCount(d->root) - 1;
1525
1526 while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
1527 --right;
1528
1529 if (bottom == -1 || right == -1)
1530 return QModelIndex(); // model is empty
1531
1532 QModelIndex current = currentIndex();
1533
1534 if (!current.isValid()) {
1535 int row = 0;
1536 int column = 0;
1537 while (column < right && isColumnHidden(d->logicalColumn(column)))
1538 ++column;
1539 while (isRowHidden(d->logicalRow(row)) && row < bottom)
1540 ++row;
1541 d->visualCursor = QPoint(column, row);
1542 return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
1543 }
1544
1545 // Update visual cursor if current index has changed.
1546 QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row()));
1547 if (visualCurrent != d->visualCursor) {
1548 if (d->hasSpans()) {
1549 QSpanCollection::Span span = d->span(current.row(), current.column());
1550 if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom()
1551 || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right())
1552 d->visualCursor = visualCurrent;
1553 } else {
1554 d->visualCursor = visualCurrent;
1555 }
1556 }
1557
1558 int visualRow = d->visualCursor.y();
1559 if (visualRow > bottom)
1560 visualRow = bottom;
1561 Q_ASSERT(visualRow != -1);
1562 int visualColumn = d->visualCursor.x();
1563 if (visualColumn > right)
1564 visualColumn = right;
1565 Q_ASSERT(visualColumn != -1);
1566
1567 if (isRightToLeft()) {
1568 if (cursorAction == MoveLeft)
1569 cursorAction = MoveRight;
1570 else if (cursorAction == MoveRight)
1571 cursorAction = MoveLeft;
1572 }
1573
1574 switch (cursorAction) {
1575 case MoveUp: {
1576 int originalRow = visualRow;
1577#ifdef QT_KEYPAD_NAVIGATION
1578 if (QApplication::keypadNavigationEnabled() && visualRow == 0)
1579 visualRow = d->visualRow(model()->rowCount() - 1) + 1;
1580 // FIXME? visualRow = bottom + 1;
1581#endif
1582 int r = d->logicalRow(visualRow);
1583 int c = d->logicalColumn(visualColumn);
1584 if (r != -1 && d->hasSpans()) {
1585 QSpanCollection::Span span = d->span(r, c);
1586 if (span.width() > 1 || span.height() > 1)
1587 visualRow = d->visualRow(span.top());
1588 }
1589 while (visualRow >= 0) {
1590 --visualRow;
1591 r = d->logicalRow(visualRow);
1592 c = d->logicalColumn(visualColumn);
1593 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1594 break;
1595 }
1596 if (visualRow < 0)
1597 visualRow = originalRow;
1598 break;
1599 }
1600 case MoveDown: {
1601 int originalRow = visualRow;
1602 if (d->hasSpans()) {
1603 QSpanCollection::Span span = d->span(current.row(), current.column());
1604 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1605 }
1606#ifdef QT_KEYPAD_NAVIGATION
1607 if (QApplication::keypadNavigationEnabled() && visualRow >= bottom)
1608 visualRow = -1;
1609#endif
1610 int r = d->logicalRow(visualRow);
1611 int c = d->logicalColumn(visualColumn);
1612 if (r != -1 && d->hasSpans()) {
1613 QSpanCollection::Span span = d->span(r, c);
1614 if (span.width() > 1 || span.height() > 1)
1615 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1616 }
1617 while (visualRow <= bottom) {
1618 ++visualRow;
1619 r = d->logicalRow(visualRow);
1620 c = d->logicalColumn(visualColumn);
1621 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1622 break;
1623 }
1624 if (visualRow > bottom)
1625 visualRow = originalRow;
1626 break;
1627 }
1628 case MovePrevious:
1629 case MoveLeft: {
1630 int originalRow = visualRow;
1631 int originalColumn = visualColumn;
1632 bool firstTime = true;
1633 bool looped = false;
1634 bool wrapped = false;
1635 do {
1636 int r = d->logicalRow(visualRow);
1637 int c = d->logicalColumn(visualColumn);
1638 if (firstTime && c != -1 && d->hasSpans()) {
1639 firstTime = false;
1640 QSpanCollection::Span span = d->span(r, c);
1641 if (span.width() > 1 || span.height() > 1)
1642 visualColumn = d->visualColumn(span.left());
1643 }
1644 while (visualColumn >= 0) {
1645 --visualColumn;
1646 r = d->logicalRow(visualRow);
1647 c = d->logicalColumn(visualColumn);
1648 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1649 break;
1650 if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) {
1651 looped = true;
1652 break;
1653 }
1654 }
1655 if (cursorAction == MoveLeft || visualColumn >= 0)
1656 break;
1657 visualColumn = right + 1;
1658 if (visualRow == 0) {
1659 wrapped = true;
1660 visualRow = bottom;
1661 } else {
1662 --visualRow;
1663 }
1664 } while (!looped);
1665 if (visualColumn < 0)
1666 visualColumn = originalColumn;
1667 break;
1668 }
1669 case MoveNext:
1670 case MoveRight: {
1671 int originalRow = visualRow;
1672 int originalColumn = visualColumn;
1673 bool firstTime = true;
1674 bool looped = false;
1675 bool wrapped = false;
1676 do {
1677 int r = d->logicalRow(visualRow);
1678 int c = d->logicalColumn(visualColumn);
1679 if (firstTime && c != -1 && d->hasSpans()) {
1680 firstTime = false;
1681 QSpanCollection::Span span = d->span(r, c);
1682 if (span.width() > 1 || span.height() > 1)
1683 visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1684 }
1685 while (visualColumn <= right) {
1686 ++visualColumn;
1687 r = d->logicalRow(visualRow);
1688 c = d->logicalColumn(visualColumn);
1689 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1690 break;
1691 if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) {
1692 looped = true;
1693 break;
1694 }
1695 }
1696 if (cursorAction == MoveRight || visualColumn <= right)
1697 break;
1698 visualColumn = -1;
1699 if (visualRow == bottom) {
1700 wrapped = true;
1701 visualRow = 0;
1702 } else {
1703 ++visualRow;
1704 }
1705 } while (!looped);
1706 if (visualColumn > right)
1707 visualColumn = originalColumn;
1708 break;
1709 }
1710 case MoveHome:
1711 visualColumn = 0;
1712 while (visualColumn < right && d->isVisualColumnHiddenOrDisabled(visualRow, visualColumn))
1713 ++visualColumn;
1714 if (modifiers & Qt::ControlModifier) {
1715 visualRow = 0;
1716 while (visualRow < bottom && d->isVisualRowHiddenOrDisabled(visualRow, visualColumn))
1717 ++visualRow;
1718 }
1719 break;
1720 case MoveEnd:
1721 visualColumn = right;
1722 if (modifiers & Qt::ControlModifier)
1723 visualRow = bottom;
1724 break;
1725 case MovePageUp: {
1726 int newRow = rowAt(visualRect(current).top() - d->viewport->height());
1727 if (newRow == -1)
1728 newRow = d->logicalRow(0);
1729 return d->model->index(newRow, current.column(), d->root);
1730 }
1731 case MovePageDown: {
1732 int newRow = rowAt(visualRect(current).bottom() + d->viewport->height());
1733 if (newRow == -1)
1734 newRow = d->logicalRow(bottom);
1735 return d->model->index(newRow, current.column(), d->root);
1736 }}
1737
1738 d->visualCursor = QPoint(visualColumn, visualRow);
1739 int logicalRow = d->logicalRow(visualRow);
1740 int logicalColumn = d->logicalColumn(visualColumn);
1741 if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
1742 return QModelIndex();
1743
1744 QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
1745 if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result))
1746 return result;
1747
1748 return QModelIndex();
1749}
1750
1751/*!
1752 \fn void QTableView::setSelection(const QRect &rect,
1753 QItemSelectionModel::SelectionFlags flags)
1754
1755 Selects the items within the given \a rect and in accordance with
1756 the specified selection \a flags.
1757*/
1758void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
1759{
1760 Q_D(QTableView);
1761 QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
1762 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
1763 QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
1764 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
1765 if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
1766 return;
1767
1768 bool verticalMoved = verticalHeader()->sectionsMoved();
1769 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1770
1771 QItemSelection selection;
1772
1773 if (d->hasSpans()) {
1774 bool expanded;
1775 int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
1776 int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1777 int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
1778 int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
1779 do {
1780 expanded = false;
1781 foreach (QSpanCollection::Span *it, d->spans.spans) {
1782 const QSpanCollection::Span &span = *it;
1783 int t = d->visualRow(span.top());
1784 int l = d->visualColumn(span.left());
1785 int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1786 int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1787 if ((t > bottom) || (l > right) || (top > b) || (left > r))
1788 continue; // no intersect
1789 if (t < top) {
1790 top = t;
1791 expanded = true;
1792 }
1793 if (l < left) {
1794 left = l;
1795 expanded = true;
1796 }
1797 if (b > bottom) {
1798 bottom = b;
1799 expanded = true;
1800 }
1801 if (r > right) {
1802 right = r;
1803 expanded = true;
1804 }
1805 if (expanded)
1806 break;
1807 }
1808 } while (expanded);
1809 for (int horizontal = left; horizontal <= right; ++horizontal) {
1810 int column = d->logicalColumn(horizontal);
1811 for (int vertical = top; vertical <= bottom; ++vertical) {
1812 int row = d->logicalRow(vertical);
1813 QModelIndex index = d->model->index(row, column, d->root);
1814 selection.append(QItemSelectionRange(index));
1815 }
1816 }
1817 } else if (verticalMoved && horizontalMoved) {
1818 int top = d->visualRow(tl.row());
1819 int left = d->visualColumn(tl.column());
1820 int bottom = d->visualRow(br.row());
1821 int right = d->visualColumn(br.column());
1822 for (int horizontal = left; horizontal <= right; ++horizontal) {
1823 int column = d->logicalColumn(horizontal);
1824 for (int vertical = top; vertical <= bottom; ++vertical) {
1825 int row = d->logicalRow(vertical);
1826 QModelIndex index = d->model->index(row, column, d->root);
1827 selection.append(QItemSelectionRange(index));
1828 }
1829 }
1830 } else if (horizontalMoved) {
1831 int left = d->visualColumn(tl.column());
1832 int right = d->visualColumn(br.column());
1833 for (int visual = left; visual <= right; ++visual) {
1834 int column = d->logicalColumn(visual);
1835 QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
1836 QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
1837 selection.append(QItemSelectionRange(topLeft, bottomRight));
1838 }
1839 } else if (verticalMoved) {
1840 int top = d->visualRow(tl.row());
1841 int bottom = d->visualRow(br.row());
1842 for (int visual = top; visual <= bottom; ++visual) {
1843 int row = d->logicalRow(visual);
1844 QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
1845 QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
1846 selection.append(QItemSelectionRange(topLeft, bottomRight));
1847 }
1848 } else { // nothing moved
1849 QItemSelectionRange range(tl, br);
1850 if (!range.isEmpty())
1851 selection.append(range);
1852 }
1853
1854 d->selectionModel->select(selection, command);
1855}
1856
1857/*!
1858 \internal
1859
1860 Returns the rectangle from the viewport of the items in the given
1861 \a selection.
1862
1863 Since 4.7, the returned region only contains rectangles intersecting
1864 (or included in) the viewport.
1865*/
1866QRegion QTableView::visualRegionForSelection(const QItemSelection &selection) const
1867{
1868 Q_D(const QTableView);
1869
1870 if (selection.isEmpty())
1871 return QRegion();
1872
1873 QRegion selectionRegion;
1874 const QRect &viewportRect = d->viewport->rect();
1875 bool verticalMoved = verticalHeader()->sectionsMoved();
1876 bool horizontalMoved = horizontalHeader()->sectionsMoved();
1877
1878 if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) {
1879 for (int i = 0; i < selection.count(); ++i) {
1880 QItemSelectionRange range = selection.at(i);
1881 if (range.parent() != d->root || !range.isValid())
1882 continue;
1883 for (int r = range.top(); r <= range.bottom(); ++r)
1884 for (int c = range.left(); c <= range.right(); ++c) {
1885 const QRect &rangeRect = visualRect(d->model->index(r, c, d->root));
1886 if (viewportRect.intersects(rangeRect))
1887 selectionRegion += rangeRect;
1888 }
1889 }
1890 } else if (horizontalMoved) {
1891 for (int i = 0; i < selection.count(); ++i) {
1892 QItemSelectionRange range = selection.at(i);
1893 if (range.parent() != d->root || !range.isValid())
1894 continue;
1895 int top = rowViewportPosition(range.top());
1896 int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1897 if (top > bottom)
1898 qSwap<int>(top, bottom);
1899 int height = bottom - top;
1900 for (int c = range.left(); c <= range.right(); ++c) {
1901 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
1902 if (viewportRect.intersects(rangeRect))
1903 selectionRegion += rangeRect;
1904 }
1905 }
1906 } else if (verticalMoved) {
1907 for (int i = 0; i < selection.count(); ++i) {
1908 QItemSelectionRange range = selection.at(i);
1909 if (range.parent() != d->root || !range.isValid())
1910 continue;
1911 int left = columnViewportPosition(range.left());
1912 int right = columnViewportPosition(range.right()) + columnWidth(range.right());
1913 if (left > right)
1914 qSwap<int>(left, right);
1915 int width = right - left;
1916 for (int r = range.top(); r <= range.bottom(); ++r) {
1917 const QRect rangeRect(left, rowViewportPosition(r), width, rowHeight(r));
1918 if (viewportRect.intersects(rangeRect))
1919 selectionRegion += rangeRect;
1920 }
1921 }
1922 } else { // nothing moved
1923 const int gridAdjust = showGrid() ? 1 : 0;
1924 for (int i = 0; i < selection.count(); ++i) {
1925 QItemSelectionRange range = selection.at(i);
1926 if (range.parent() != d->root || !range.isValid())
1927 continue;
1928 d->trimHiddenSelections(&range);
1929
1930 const int rtop = rowViewportPosition(range.top());
1931 const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
1932 int rleft;
1933 int rright;
1934 if (isLeftToRight()) {
1935 rleft = columnViewportPosition(range.left());
1936 rright = columnViewportPosition(range.right()) + columnWidth(range.right());
1937 } else {
1938 rleft = columnViewportPosition(range.right());
1939 rright = columnViewportPosition(range.left()) + columnWidth(range.left());
1940 }
1941 const QRect rangeRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust));
1942 if (viewportRect.intersects(rangeRect))
1943 selectionRegion += rangeRect;
1944 if (d->hasSpans()) {
1945 foreach (QSpanCollection::Span *s,
1946 d->spans.spansInRect(range.left(), range.top(), range.width(), range.height())) {
1947 if (range.contains(s->top(), s->left(), range.parent())) {
1948 const QRect &visualSpanRect = d->visualSpanRect(*s);
1949 if (viewportRect.intersects(visualSpanRect))
1950 selectionRegion += visualSpanRect;
1951 }
1952 }
1953 }
1954 }
1955 }
1956
1957 return selectionRegion;
1958}
1959
1960
1961/*!
1962 \reimp
1963*/
1964QModelIndexList QTableView::selectedIndexes() const
1965{
1966 Q_D(const QTableView);
1967 QModelIndexList viewSelected;
1968 QModelIndexList modelSelected;
1969 if (d->selectionModel)
1970 modelSelected = d->selectionModel->selectedIndexes();
1971 for (int i = 0; i < modelSelected.count(); ++i) {
1972 QModelIndex index = modelSelected.at(i);
1973 if (!isIndexHidden(index) && index.parent() == d->root)
1974 viewSelected.append(index);
1975 }
1976 return viewSelected;
1977}
1978
1979
1980/*!
1981 This slot is called whenever rows are added or deleted. The
1982 previous number of rows is specified by \a oldCount, and the new
1983 number of rows is specified by \a newCount.
1984*/
1985void QTableView::rowCountChanged(int /*oldCount*/, int /*newCount*/ )
1986{
1987 Q_D(QTableView);
1988 d->doDelayedItemsLayout();
1989}
1990
1991/*!
1992 This slot is called whenever columns are added or deleted. The
1993 previous number of columns is specified by \a oldCount, and the new
1994 number of columns is specified by \a newCount.
1995*/
1996void QTableView::columnCountChanged(int, int)
1997{
1998 Q_D(QTableView);
1999 updateGeometries();
2000 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem)
2001 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
2002 else
2003 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
2004 d->viewport->update();
2005}
2006
2007/*!
2008 \reimp
2009*/
2010void QTableView::updateGeometries()
2011{
2012 Q_D(QTableView);
2013 if (d->geometryRecursionBlock)
2014 return;
2015 d->geometryRecursionBlock = true;
2016
2017 int width = 0;
2018 if (!d->verticalHeader->isHidden()) {
2019 width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
2020 width = qMin(width, d->verticalHeader->maximumWidth());
2021 }
2022 int height = 0;
2023 if (!d->horizontalHeader->isHidden()) {
2024 height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
2025 height = qMin(height, d->horizontalHeader->maximumHeight());
2026 }
2027 bool reverse = isRightToLeft();
2028 if (reverse)
2029 setViewportMargins(0, height, width, 0);
2030 else
2031 setViewportMargins(width, height, 0, 0);
2032
2033 // update headers
2034
2035 QRect vg = d->viewport->geometry();
2036
2037 int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
2038 d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
2039 if (d->verticalHeader->isHidden())
2040 QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
2041
2042 int horizontalTop = vg.top() - height;
2043 d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
2044 if (d->horizontalHeader->isHidden())
2045 QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
2046
2047 // update cornerWidget
2048 if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
2049 d->cornerWidget->setHidden(true);
2050 } else {
2051 d->cornerWidget->setHidden(false);
2052 d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
2053 }
2054
2055 // update scroll bars
2056
2057 // ### move this block into the if
2058 QSize vsize = d->viewport->size();
2059 QSize max = maximumViewportSize();
2060 uint horizontalLength = d->horizontalHeader->length();
2061 uint verticalLength = d->verticalHeader->length();
2062 if ((uint)max.width() >= horizontalLength && (uint)max.height() >= verticalLength)
2063 vsize = max;
2064
2065 // horizontal scroll bar
2066 const int columnCount = d->horizontalHeader->count();
2067 const int viewportWidth = vsize.width();
2068 int columnsInViewport = 0;
2069 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
2070 int logical = d->horizontalHeader->logicalIndex(column);
2071 if (!d->horizontalHeader->isSectionHidden(logical)) {
2072 width += d->horizontalHeader->sectionSize(logical);
2073 if (width > viewportWidth)
2074 break;
2075 ++columnsInViewport;
2076 }
2077 }
2078 columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column
2079
2080 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2081 const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
2082 horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
2083 horizontalScrollBar()->setPageStep(columnsInViewport);
2084 if (columnsInViewport >= visibleColumns)
2085 d->horizontalHeader->setOffset(0);
2086 horizontalScrollBar()->setSingleStep(1);
2087 } else { // ScrollPerPixel
2088 horizontalScrollBar()->setPageStep(vsize.width());
2089 horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
2090 horizontalScrollBar()->setSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
2091 }
2092
2093 // vertical scroll bar
2094 const int rowCount = d->verticalHeader->count();
2095 const int viewportHeight = vsize.height();
2096 int rowsInViewport = 0;
2097 for (int height = 0, row = rowCount - 1; row >= 0; --row) {
2098 int logical = d->verticalHeader->logicalIndex(row);
2099 if (!d->verticalHeader->isSectionHidden(logical)) {
2100 height += d->verticalHeader->sectionSize(logical);
2101 if (height > viewportHeight)
2102 break;
2103 ++rowsInViewport;
2104 }
2105 }
2106 rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row
2107
2108 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2109 const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
2110 verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
2111 verticalScrollBar()->setPageStep(rowsInViewport);
2112 if (rowsInViewport >= visibleRows)
2113 d->verticalHeader->setOffset(0);
2114 verticalScrollBar()->setSingleStep(1);
2115 } else { // ScrollPerPixel
2116 verticalScrollBar()->setPageStep(vsize.height());
2117 verticalScrollBar()->setRange(0, verticalLength - vsize.height());
2118 verticalScrollBar()->setSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
2119 }
2120
2121 d->geometryRecursionBlock = false;
2122 QAbstractItemView::updateGeometries();
2123}
2124
2125/*!
2126 Returns the size hint for the given \a row's height or -1 if there
2127 is no model.
2128
2129 If you need to set the height of a given row to a fixed value, call
2130 QHeaderView::resizeSection() on the table's vertical header.
2131
2132 If you reimplement this function in a subclass, note that the value you
2133 return is only used when resizeRowToContents() is called. In that case,
2134 if a larger row height is required by either the vertical header or
2135 the item delegate, that width will be used instead.
2136
2137 \sa QWidget::sizeHint, verticalHeader()
2138*/
2139int QTableView::sizeHintForRow(int row) const
2140{
2141 Q_D(const QTableView);
2142
2143 if (!model())
2144 return -1;
2145
2146 ensurePolished();
2147
2148 int left = qMax(0, d->horizontalHeader->visualIndexAt(0));
2149 int right = d->horizontalHeader->visualIndexAt(d->viewport->width());
2150 if (right == -1) // the table don't have enough columns to fill the viewport
2151 right = d->model->columnCount(d->root) - 1;
2152
2153 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2154
2155 int hint = 0;
2156 QModelIndex index;
2157 for (int column = left; column <= right; ++column) {
2158 int logicalColumn = d->horizontalHeader->logicalIndex(column);
2159 if (d->horizontalHeader->isSectionHidden(logicalColumn))
2160 continue;
2161 index = d->model->index(row, logicalColumn, d->root);
2162 if (d->wrapItemText) {// for wrapping boundaries
2163 option.rect.setY(rowViewportPosition(index.row()));
2164 option.rect.setHeight(rowHeight(index.row()));
2165 option.rect.setX(columnViewportPosition(index.column()));
2166 option.rect.setWidth(columnWidth(index.column()));
2167 }
2168
2169 QWidget *editor = d->editorForIndex(index).editor;
2170 if (editor && d->persistent.contains(editor)) {
2171 hint = qMax(hint, editor->sizeHint().height());
2172 int min = editor->minimumSize().height();
2173 int max = editor->maximumSize().height();
2174 hint = qBound(min, hint, max);
2175 }
2176
2177 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).height());
2178 }
2179
2180 return d->showGrid ? hint + 1 : hint;
2181}
2182
2183/*!
2184 Returns the size hint for the given \a column's width or -1 if
2185 there is no model.
2186
2187 If you need to set the width of a given column to a fixed value, call
2188 QHeaderView::resizeSection() on the table's horizontal header.
2189
2190 If you reimplement this function in a subclass, note that the value you
2191 return will be used when resizeColumnToContents() or
2192 QHeaderView::resizeSections() is called. If a larger column width is
2193 required by either the horizontal header or the item delegate, the larger
2194 width will be used instead.
2195
2196 \sa QWidget::sizeHint, horizontalHeader()
2197*/
2198int QTableView::sizeHintForColumn(int column) const
2199{
2200 Q_D(const QTableView);
2201
2202 if (!model())
2203 return -1;
2204
2205 ensurePolished();
2206
2207 int top = qMax(0, d->verticalHeader->visualIndexAt(0));
2208 int bottom = d->verticalHeader->visualIndexAt(d->viewport->height());
2209 if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
2210 bottom = d->model->rowCount(d->root) - 1;
2211
2212 QStyleOptionViewItemV4 option = d->viewOptionsV4();
2213
2214 int hint = 0;
2215 QModelIndex index;
2216 for (int row = top; row <= bottom; ++row) {
2217 int logicalRow = d->verticalHeader->logicalIndex(row);
2218 if (d->verticalHeader->isSectionHidden(logicalRow))
2219 continue;
2220 index = d->model->index(logicalRow, column, d->root);
2221
2222 QWidget *editor = d->editorForIndex(index).editor;
2223 if (editor && d->persistent.contains(editor)) {
2224 hint = qMax(hint, editor->sizeHint().width());
2225 int min = editor->minimumSize().width();
2226 int max = editor->maximumSize().width();
2227 hint = qBound(min, hint, max);
2228 }
2229
2230 hint = qMax(hint, itemDelegate(index)->sizeHint(option, index).width());
2231 }
2232
2233 return d->showGrid ? hint + 1 : hint;
2234}
2235
2236/*!
2237 Returns the y-coordinate in contents coordinates of the given \a
2238 row.
2239*/
2240int QTableView::rowViewportPosition(int row) const
2241{
2242 Q_D(const QTableView);
2243 return d->verticalHeader->sectionViewportPosition(row);
2244}
2245
2246/*!
2247 Returns the row in which the given y-coordinate, \a y, in contents
2248 coordinates is located.
2249
2250 \note This function returns -1 if the given coordinate is not valid
2251 (has no row).
2252
2253 \sa columnAt()
2254*/
2255int QTableView::rowAt(int y) const
2256{
2257 Q_D(const QTableView);
2258 return d->verticalHeader->logicalIndexAt(y);
2259}
2260
2261/*!
2262 \since 4.1
2263
2264 Sets the height of the given \a row to be \a height.
2265*/
2266void QTableView::setRowHeight(int row, int height)
2267{
2268 Q_D(const QTableView);
2269 d->verticalHeader->resizeSection(row, height);
2270}
2271
2272/*!
2273 Returns the height of the given \a row.
2274
2275 \sa resizeRowToContents(), columnWidth()
2276*/
2277int QTableView::rowHeight(int row) const
2278{
2279 Q_D(const QTableView);
2280 return d->verticalHeader->sectionSize(row);
2281}
2282
2283/*!
2284 Returns the x-coordinate in contents coordinates of the given \a
2285 column.
2286*/
2287int QTableView::columnViewportPosition(int column) const
2288{
2289 Q_D(const QTableView);
2290 return d->horizontalHeader->sectionViewportPosition(column);
2291}
2292
2293/*!
2294 Returns the column in which the given x-coordinate, \a x, in contents
2295 coordinates is located.
2296
2297 \note This function returns -1 if the given coordinate is not valid
2298 (has no column).
2299
2300 \sa rowAt()
2301*/
2302int QTableView::columnAt(int x) const
2303{
2304 Q_D(const QTableView);
2305 return d->horizontalHeader->logicalIndexAt(x);
2306}
2307
2308/*!
2309 \since 4.1
2310
2311 Sets the width of the given \a column to be \a width.
2312*/
2313void QTableView::setColumnWidth(int column, int width)
2314{
2315 Q_D(const QTableView);
2316 d->horizontalHeader->resizeSection(column, width);
2317}
2318
2319/*!
2320 Returns the width of the given \a column.
2321
2322 \sa resizeColumnToContents(), rowHeight()
2323*/
2324int QTableView::columnWidth(int column) const
2325{
2326 Q_D(const QTableView);
2327 return d->horizontalHeader->sectionSize(column);
2328}
2329
2330/*!
2331 Returns true if the given \a row is hidden; otherwise returns false.
2332
2333 \sa isColumnHidden()
2334*/
2335bool QTableView::isRowHidden(int row) const
2336{
2337 Q_D(const QTableView);
2338 return d->verticalHeader->isSectionHidden(row);
2339}
2340
2341/*!
2342 If \a hide is true \a row will be hidden, otherwise it will be shown.
2343
2344 \sa setColumnHidden()
2345*/
2346void QTableView::setRowHidden(int row, bool hide)
2347{
2348 Q_D(QTableView);
2349 if (row < 0 || row >= d->verticalHeader->count())
2350 return;
2351 d->verticalHeader->setSectionHidden(row, hide);
2352}
2353
2354/*!
2355 Returns true if the given \a column is hidden; otherwise returns false.
2356
2357 \sa isRowHidden()
2358*/
2359bool QTableView::isColumnHidden(int column) const
2360{
2361 Q_D(const QTableView);
2362 return d->horizontalHeader->isSectionHidden(column);
2363}
2364
2365/*!
2366 If \a hide is true the given \a column will be hidden; otherwise it
2367 will be shown.
2368
2369 \sa setRowHidden()
2370*/
2371void QTableView::setColumnHidden(int column, bool hide)
2372{
2373 Q_D(QTableView);
2374 if (column < 0 || column >= d->horizontalHeader->count())
2375 return;
2376 d->horizontalHeader->setSectionHidden(column, hide);
2377}
2378
2379/*!
2380 \since 4.2
2381 \property QTableView::sortingEnabled
2382 \brief whether sorting is enabled
2383
2384 If this property is true, sorting is enabled for the table. If
2385 this property is false, sorting is not enabled. The default value
2386 is false.
2387
2388 \note. Setting the property to true with setSortingEnabled()
2389 immediately triggers a call to sortByColumn() with the current
2390 sort section and order.
2391
2392 \sa sortByColumn()
2393*/
2394
2395/*!
2396 If \a enabled true enables sorting for the table and immediately
2397 trigger a call to sortByColumn() with the current sort section and
2398 order
2399 */
2400void QTableView::setSortingEnabled(bool enable)
2401{
2402 Q_D(QTableView);
2403 d->sortingEnabled = enable;
2404 horizontalHeader()->setSortIndicatorShown(enable);
2405 if (enable) {
2406 disconnect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2407 this, SLOT(_q_selectColumn(int)));
2408 disconnect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2409 this, SLOT(selectColumn(int)));
2410 connect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2411 this, SLOT(sortByColumn(int)), Qt::UniqueConnection);
2412 sortByColumn(horizontalHeader()->sortIndicatorSection(),
2413 horizontalHeader()->sortIndicatorOrder());
2414 } else {
2415 connect(d->horizontalHeader, SIGNAL(sectionEntered(int)),
2416 this, SLOT(_q_selectColumn(int)), Qt::UniqueConnection);
2417 connect(horizontalHeader(), SIGNAL(sectionPressed(int)),
2418 this, SLOT(selectColumn(int)), Qt::UniqueConnection);
2419 disconnect(horizontalHeader(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)),
2420 this, SLOT(sortByColumn(int)));
2421 }
2422}
2423
2424bool QTableView::isSortingEnabled() const
2425{
2426 Q_D(const QTableView);
2427 return d->sortingEnabled;
2428}
2429
2430/*!
2431 \property QTableView::showGrid
2432 \brief whether the grid is shown
2433
2434 If this property is true a grid is drawn for the table; if the
2435 property is false, no grid is drawn. The default value is true.
2436*/
2437bool QTableView::showGrid() const
2438{
2439 Q_D(const QTableView);
2440 return d->showGrid;
2441}
2442
2443void QTableView::setShowGrid(bool show)
2444{
2445 Q_D(QTableView);
2446 if (d->showGrid != show) {
2447 d->showGrid = show;
2448 d->viewport->update();
2449 }
2450}
2451
2452/*!
2453 \property QTableView::gridStyle
2454 \brief the pen style used to draw the grid.
2455
2456 This property holds the style used when drawing the grid (see \l{showGrid}).
2457*/
2458Qt::PenStyle QTableView::gridStyle() const
2459{
2460 Q_D(const QTableView);
2461 return d->gridStyle;
2462}
2463
2464void QTableView::setGridStyle(Qt::PenStyle style)
2465{
2466 Q_D(QTableView);
2467 if (d->gridStyle != style) {
2468 d->gridStyle = style;
2469 d->viewport->update();
2470 }
2471}
2472
2473/*!
2474 \property QTableView::wordWrap
2475 \brief the item text word-wrapping policy
2476 \since 4.3
2477
2478 If this property is true then the item text is wrapped where
2479 necessary at word-breaks; otherwise it is not wrapped at all.
2480 This property is true by default.
2481
2482 Note that even of wrapping is enabled, the cell will not be
2483 expanded to fit all text. Ellipsis will be inserted according to
2484 the current \l{QAbstractItemView::}{textElideMode}.
2485
2486*/
2487void QTableView::setWordWrap(bool on)
2488{
2489 Q_D(QTableView);
2490 if (d->wrapItemText == on)
2491 return;
2492 d->wrapItemText = on;
2493 QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
2494 QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
2495}
2496
2497bool QTableView::wordWrap() const
2498{
2499 Q_D(const QTableView);
2500 return d->wrapItemText;
2501}
2502
2503/*!
2504 \property QTableView::cornerButtonEnabled
2505 \brief whether the button in the top-left corner is enabled
2506 \since 4.3
2507
2508 If this property is true then button in the top-left corner
2509 of the table view is enabled. Clicking on this button will
2510 select all the cells in the table view.
2511
2512 This property is true by default.
2513*/
2514void QTableView::setCornerButtonEnabled(bool enable)
2515{
2516 Q_D(QTableView);
2517 d->cornerWidget->setEnabled(enable);
2518}
2519
2520bool QTableView::isCornerButtonEnabled() const
2521{
2522 Q_D(const QTableView);
2523 return d->cornerWidget->isEnabled();
2524}
2525
2526/*!
2527 \internal
2528
2529 Returns the rectangle on the viewport occupied by the given \a
2530 index.
2531 If the index is hidden in the view it will return a null QRect.
2532*/
2533QRect QTableView::visualRect(const QModelIndex &index) const
2534{
2535 Q_D(const QTableView);
2536 if (!d->isIndexValid(index) || index.parent() != d->root
2537 || (!d->hasSpans() && isIndexHidden(index)))
2538 return QRect();
2539
2540 d->executePostedLayout();
2541
2542 if (d->hasSpans()) {
2543 QSpanCollection::Span span = d->span(index.row(), index.column());
2544 return d->visualSpanRect(span);
2545 }
2546
2547 int rowp = rowViewportPosition(index.row());
2548 int rowh = rowHeight(index.row());
2549 int colp = columnViewportPosition(index.column());
2550 int colw = columnWidth(index.column());
2551
2552 const int i = showGrid() ? 1 : 0;
2553 return QRect(colp, rowp, colw - i, rowh - i);
2554}
2555
2556/*!
2557 \internal
2558
2559 Makes sure that the given \a item is visible in the table view,
2560 scrolling if necessary.
2561*/
2562void QTableView::scrollTo(const QModelIndex &index, ScrollHint hint)
2563{
2564 Q_D(QTableView);
2565
2566 // check if we really need to do anything
2567 if (!d->isIndexValid(index)
2568 || (d->model->parent(index) != d->root)
2569 || isRowHidden(index.row()) || isColumnHidden(index.column()))
2570 return;
2571
2572 QSpanCollection::Span span;
2573 if (d->hasSpans())
2574 span = d->span(index.row(), index.column());
2575
2576 // Adjust horizontal position
2577
2578 int viewportWidth = d->viewport->width();
2579 int horizontalOffset = d->horizontalHeader->offset();
2580 int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
2581 int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
2582 int cellWidth = d->hasSpans()
2583 ? d->columnSpanWidth(index.column(), span.width())
2584 : d->horizontalHeader->sectionSize(index.column());
2585
2586 if (horizontalScrollMode() == QAbstractItemView::ScrollPerItem) {
2587
2588 bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
2589 bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
2590
2591 if (hint == PositionAtCenter || positionAtRight) {
2592 int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
2593 int x = cellWidth;
2594 while (horizontalIndex > 0) {
2595 x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
2596 if (x > w)
2597 break;
2598 --horizontalIndex;
2599 }
2600 }
2601
2602 if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
2603 int hiddenSections = 0;
2604 if (d->horizontalHeader->sectionsHidden()) {
2605 for (int s = horizontalIndex - 1; s >= 0; --s) {
2606 int column = d->horizontalHeader->logicalIndex(s);
2607 if (d->horizontalHeader->isSectionHidden(column))
2608 ++hiddenSections;
2609 }
2610 }
2611 horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
2612 }
2613
2614 } else { // ScrollPerPixel
2615 if (hint == PositionAtCenter) {
2616 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
2617 } else {
2618 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
2619 horizontalScrollBar()->setValue(horizontalPosition);
2620 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
2621 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
2622 }
2623 }
2624
2625 // Adjust vertical position
2626
2627 int viewportHeight = d->viewport->height();
2628 int verticalOffset = d->verticalHeader->offset();
2629 int verticalPosition = d->verticalHeader->sectionPosition(index.row());
2630 int verticalIndex = d->verticalHeader->visualIndex(index.row());
2631 int cellHeight = d->hasSpans()
2632 ? d->rowSpanHeight(index.row(), span.height())
2633 : d->verticalHeader->sectionSize(index.row());
2634
2635 if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
2636 if (hint == EnsureVisible)
2637 hint = PositionAtTop;
2638 } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
2639 if (hint == EnsureVisible)
2640 hint = PositionAtBottom;
2641 }
2642
2643 if (verticalScrollMode() == QAbstractItemView::ScrollPerItem) {
2644
2645 if (hint == PositionAtBottom || hint == PositionAtCenter) {
2646 int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
2647 int y = cellHeight;
2648 while (verticalIndex > 0) {
2649 int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
2650 y += d->verticalHeader->sectionSize(row);
2651 if (y > h)
2652 break;
2653 --verticalIndex;
2654 }
2655 }
2656
2657 if (hint == PositionAtBottom || hint == PositionAtCenter || hint == PositionAtTop) {
2658 int hiddenSections = 0;
2659 if (d->verticalHeader->sectionsHidden()) {
2660 for (int s = verticalIndex - 1; s >= 0; --s) {
2661 int row = d->verticalHeader->logicalIndex(s);
2662 if (d->verticalHeader->isSectionHidden(row))
2663 ++hiddenSections;
2664 }
2665 }
2666 verticalScrollBar()->setValue(verticalIndex - hiddenSections);
2667 }
2668
2669 } else { // ScrollPerPixel
2670 if (hint == PositionAtTop) {
2671 verticalScrollBar()->setValue(verticalPosition);
2672 } else if (hint == PositionAtBottom) {
2673 verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
2674 } else if (hint == PositionAtCenter) {
2675 verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
2676 }
2677 }
2678
2679 update(index);
2680}
2681
2682/*!
2683 This slot is called to change the height of the given \a row. The
2684 old height is specified by \a oldHeight, and the new height by \a
2685 newHeight.
2686
2687 \sa columnResized()
2688*/
2689void QTableView::rowResized(int row, int, int)
2690{
2691 Q_D(QTableView);
2692 d->rowsToUpdate.append(row);
2693 if (d->rowResizeTimerID == 0)
2694 d->rowResizeTimerID = startTimer(0);
2695}
2696
2697/*!
2698 This slot is called to change the width of the given \a column.
2699 The old width is specified by \a oldWidth, and the new width by \a
2700 newWidth.
2701
2702 \sa rowResized()
2703*/
2704void QTableView::columnResized(int column, int, int)
2705{
2706 Q_D(QTableView);
2707 d->columnsToUpdate.append(column);
2708 if (d->columnResizeTimerID == 0)
2709 d->columnResizeTimerID = startTimer(0);
2710}
2711
2712/*!
2713 \reimp
2714 */
2715void QTableView::timerEvent(QTimerEvent *event)
2716{
2717 Q_D(QTableView);
2718
2719 if (event->timerId() == d->columnResizeTimerID) {
2720 updateGeometries();
2721 killTimer(d->columnResizeTimerID);
2722 d->columnResizeTimerID = 0;
2723
2724 QRect rect;
2725 int viewportHeight = d->viewport->height();
2726 int viewportWidth = d->viewport->width();
2727 if (d->hasSpans()) {
2728 rect = QRect(0, 0, viewportWidth, viewportHeight);
2729 } else {
2730 for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
2731 int column = d->columnsToUpdate.at(i);
2732 int x = columnViewportPosition(column);
2733 if (isRightToLeft())
2734 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
2735 else
2736 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
2737 }
2738 }
2739
2740 d->viewport->update(rect.normalized());
2741 d->columnsToUpdate.clear();
2742 }
2743
2744 if (event->timerId() == d->rowResizeTimerID) {
2745 updateGeometries();
2746 killTimer(d->rowResizeTimerID);
2747 d->rowResizeTimerID = 0;
2748
2749 int viewportHeight = d->viewport->height();
2750 int viewportWidth = d->viewport->width();
2751 int top;
2752 if (d->hasSpans()) {
2753 top = 0;
2754 } else {
2755 top = viewportHeight;
2756 for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
2757 int y = rowViewportPosition(d->rowsToUpdate.at(i));
2758 top = qMin(top, y);
2759 }
2760 }
2761
2762 d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
2763 d->rowsToUpdate.clear();
2764 }
2765
2766 QAbstractItemView::timerEvent(event);
2767}
2768
2769/*!
2770 This slot is called to change the index of the given \a row in the
2771 table view. The old index is specified by \a oldIndex, and the new
2772 index by \a newIndex.
2773
2774 \sa columnMoved()
2775*/
2776void QTableView::rowMoved(int, int oldIndex, int newIndex)
2777{
2778 Q_D(QTableView);
2779
2780 updateGeometries();
2781 int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
2782 int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
2783 if (d->hasSpans()) {
2784 d->viewport->update();
2785 } else {
2786 int oldTop = rowViewportPosition(logicalOldIndex);
2787 int newTop = rowViewportPosition(logicalNewIndex);
2788 int oldBottom = oldTop + rowHeight(logicalOldIndex);
2789 int newBottom = newTop + rowHeight(logicalNewIndex);
2790 int top = qMin(oldTop, newTop);
2791 int bottom = qMax(oldBottom, newBottom);
2792 int height = bottom - top;
2793 d->viewport->update(0, top, d->viewport->width(), height);
2794 }
2795}
2796
2797/*!
2798 This slot is called to change the index of the given \a column in
2799 the table view. The old index is specified by \a oldIndex, and
2800 the new index by \a newIndex.
2801
2802 \sa rowMoved()
2803*/
2804void QTableView::columnMoved(int, int oldIndex, int newIndex)
2805{
2806 Q_D(QTableView);
2807
2808 updateGeometries();
2809 int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
2810 int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
2811 if (d->hasSpans()) {
2812 d->viewport->update();
2813 } else {
2814 int oldLeft = columnViewportPosition(logicalOldIndex);
2815 int newLeft = columnViewportPosition(logicalNewIndex);
2816 int oldRight = oldLeft + columnWidth(logicalOldIndex);
2817 int newRight = newLeft + columnWidth(logicalNewIndex);
2818 int left = qMin(oldLeft, newLeft);
2819 int right = qMax(oldRight, newRight);
2820 int width = right - left;
2821 d->viewport->update(left, 0, width, d->viewport->height());
2822 }
2823}
2824
2825/*!
2826 Selects the given \a row in the table view if the current
2827 SelectionMode and SelectionBehavior allows rows to be selected.
2828
2829 \sa selectColumn()
2830*/
2831void QTableView::selectRow(int row)
2832{
2833 Q_D(QTableView);
2834 d->selectRow(row, true);
2835}
2836
2837/*!
2838 Selects the given \a column in the table view if the current
2839 SelectionMode and SelectionBehavior allows columns to be selected.
2840
2841 \sa selectRow()
2842*/
2843void QTableView::selectColumn(int column)
2844{
2845 Q_D(QTableView);
2846 d->selectColumn(column, true);
2847}
2848
2849/*!
2850 Hide the given \a row.
2851
2852 \sa showRow() hideColumn()
2853*/
2854void QTableView::hideRow(int row)
2855{
2856 Q_D(QTableView);
2857 d->verticalHeader->hideSection(row);
2858}
2859
2860/*!
2861 Hide the given \a column.
2862
2863 \sa showColumn() hideRow()
2864*/
2865void QTableView::hideColumn(int column)
2866{
2867 Q_D(QTableView);
2868 d->horizontalHeader->hideSection(column);
2869}
2870
2871/*!
2872 Show the given \a row.
2873
2874 \sa hideRow() showColumn()
2875*/
2876void QTableView::showRow(int row)
2877{
2878 Q_D(QTableView);
2879 d->verticalHeader->showSection(row);
2880}
2881
2882/*!
2883 Show the given \a column.
2884
2885 \sa hideColumn() showRow()
2886*/
2887void QTableView::showColumn(int column)
2888{
2889 Q_D(QTableView);
2890 d->horizontalHeader->showSection(column);
2891}
2892
2893/*!
2894 Resizes the given \a row based on the size hints of the delegate
2895 used to render each item in the row.
2896*/
2897void QTableView::resizeRowToContents(int row)
2898{
2899 Q_D(QTableView);
2900 int content = sizeHintForRow(row);
2901 int header = d->verticalHeader->sectionSizeHint(row);
2902 d->verticalHeader->resizeSection(row, qMax(content, header));
2903}
2904
2905/*!
2906 Resizes all rows based on the size hints of the delegate
2907 used to render each item in the rows.
2908*/
2909void QTableView::resizeRowsToContents()
2910{
2911 Q_D(QTableView);
2912 d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
2913}
2914
2915/*!
2916 Resizes the given \a column based on the size hints of the delegate
2917 used to render each item in the column.
2918
2919 \note Only visible columns will be resized. Reimplement sizeHintForColumn()
2920 to resize hidden columns as well.
2921*/
2922void QTableView::resizeColumnToContents(int column)
2923{
2924 Q_D(QTableView);
2925 int content = sizeHintForColumn(column);
2926 int header = d->horizontalHeader->sectionSizeHint(column);
2927 d->horizontalHeader->resizeSection(column, qMax(content, header));
2928}
2929
2930/*!
2931 Resizes all columns based on the size hints of the delegate
2932 used to render each item in the columns.
2933*/
2934void QTableView::resizeColumnsToContents()
2935{
2936 Q_D(QTableView);
2937 d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
2938}
2939
2940/*!
2941 \obsolete
2942 \overload
2943
2944 Sorts the model by the values in the given \a column.
2945*/
2946void QTableView::sortByColumn(int column)
2947{
2948 Q_D(QTableView);
2949 if (column == -1)
2950 return;
2951 d->model->sort(column, d->horizontalHeader->sortIndicatorOrder());
2952}
2953
2954/*!
2955 \since 4.2
2956
2957 Sorts the model by the values in the given \a column in the given \a order.
2958
2959 \sa sortingEnabled
2960 */
2961void QTableView::sortByColumn(int column, Qt::SortOrder order)
2962{
2963 Q_D(QTableView);
2964 d->horizontalHeader->setSortIndicator(column, order);
2965 sortByColumn(column);
2966}
2967
2968/*!
2969 \internal
2970*/
2971void QTableView::verticalScrollbarAction(int action)
2972{
2973 QAbstractItemView::verticalScrollbarAction(action);
2974}
2975
2976/*!
2977 \internal
2978*/
2979void QTableView::horizontalScrollbarAction(int action)
2980{
2981 QAbstractItemView::horizontalScrollbarAction(action);
2982}
2983
2984/*!
2985 \reimp
2986*/
2987bool QTableView::isIndexHidden(const QModelIndex &index) const
2988{
2989 Q_D(const QTableView);
2990 Q_ASSERT(d->isIndexValid(index));
2991 if (isRowHidden(index.row()) || isColumnHidden(index.column()))
2992 return true;
2993 if (d->hasSpans()) {
2994 QSpanCollection::Span span = d->span(index.row(), index.column());
2995 return !((span.top() == index.row()) && (span.left() == index.column()));
2996 }
2997 return false;
2998}
2999
3000/*!
3001 \fn void QTableView::setSpan(int row, int column, int rowSpanCount, int columnSpanCount)
3002 \since 4.2
3003
3004 Sets the span of the table element at (\a row, \a column) to the number of
3005 rows and columns specified by (\a rowSpanCount, \a columnSpanCount).
3006
3007 \sa rowSpan(), columnSpan()
3008*/
3009void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
3010{
3011 Q_D(QTableView);
3012 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
3013 return;
3014 d->setSpan(row, column, rowSpan, columnSpan);
3015 d->viewport->update();
3016}
3017
3018/*!
3019 \since 4.2
3020
3021 Returns the row span of the table element at (\a row, \a column).
3022 The default is 1.
3023
3024 \sa setSpan(), columnSpan()
3025*/
3026int QTableView::rowSpan(int row, int column) const
3027{
3028 Q_D(const QTableView);
3029 return d->rowSpan(row, column);
3030}
3031
3032/*!
3033 \since 4.2
3034
3035 Returns the column span of the table element at (\a row, \a
3036 column). The default is 1.
3037
3038 \sa setSpan(), rowSpan()
3039*/
3040int QTableView::columnSpan(int row, int column) const
3041{
3042 Q_D(const QTableView);
3043 return d->columnSpan(row, column);
3044}
3045
3046/*!
3047 \since 4.4
3048
3049 Removes all row and column spans in the table view.
3050
3051 \sa setSpan()
3052*/
3053
3054void QTableView::clearSpans()
3055{
3056 Q_D(QTableView);
3057 d->spans.clear();
3058 d->viewport->update();
3059}
3060
3061void QTableViewPrivate::_q_selectRow(int row)
3062{
3063 selectRow(row, false);
3064}
3065
3066void QTableViewPrivate::_q_selectColumn(int column)
3067{
3068 selectColumn(column, false);
3069}
3070
3071void QTableViewPrivate::selectRow(int row, bool anchor)
3072{
3073 Q_Q(QTableView);
3074
3075 if (q->selectionBehavior() == QTableView::SelectColumns
3076 || (q->selectionMode() == QTableView::SingleSelection
3077 && q->selectionBehavior() == QTableView::SelectItems))
3078 return;
3079
3080 if (row >= 0 && row < model->rowCount(root)) {
3081 int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
3082 QModelIndex index = model->index(row, column, root);
3083 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3084 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3085 if ((anchor && !(command & QItemSelectionModel::Current))
3086 || (q->selectionMode() == QTableView::SingleSelection))
3087 rowSectionAnchor = row;
3088
3089 if (q->selectionMode() != QTableView::SingleSelection
3090 && command.testFlag(QItemSelectionModel::Toggle)) {
3091 if (anchor)
3092 ctrlDragSelectionFlag = verticalHeader->selectionModel()->selectedRows().contains(index)
3093 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3094 command &= ~QItemSelectionModel::Toggle;
3095 command |= ctrlDragSelectionFlag;
3096 if (!anchor)
3097 command |= QItemSelectionModel::Current;
3098 }
3099
3100 QModelIndex tl = model->index(qMin(rowSectionAnchor, row), 0, root);
3101 QModelIndex br = model->index(qMax(rowSectionAnchor, row), model->columnCount(root) - 1, root);
3102 if (verticalHeader->sectionsMoved() && tl.row() != br.row())
3103 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3104 else
3105 selectionModel->select(QItemSelection(tl, br), command);
3106 }
3107}
3108
3109void QTableViewPrivate::selectColumn(int column, bool anchor)
3110{
3111 Q_Q(QTableView);
3112
3113 if (q->selectionBehavior() == QTableView::SelectRows
3114 || (q->selectionMode() == QTableView::SingleSelection
3115 && q->selectionBehavior() == QTableView::SelectItems))
3116 return;
3117
3118 if (column >= 0 && column < model->columnCount(root)) {
3119 int row = verticalHeader->logicalIndexAt(0);
3120 QModelIndex index = model->index(row, column, root);
3121 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3122 selectionModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
3123 if ((anchor && !(command & QItemSelectionModel::Current))
3124 || (q->selectionMode() == QTableView::SingleSelection))
3125 columnSectionAnchor = column;
3126
3127 if (q->selectionMode() != QTableView::SingleSelection
3128 && command.testFlag(QItemSelectionModel::Toggle)) {
3129 if (anchor)
3130 ctrlDragSelectionFlag = horizontalHeader->selectionModel()->selectedColumns().contains(index)
3131 ? QItemSelectionModel::Deselect : QItemSelectionModel::Select;
3132 command &= ~QItemSelectionModel::Toggle;
3133 command |= ctrlDragSelectionFlag;
3134 if (!anchor)
3135 command |= QItemSelectionModel::Current;
3136 }
3137
3138 QModelIndex tl = model->index(0, qMin(columnSectionAnchor, column), root);
3139 QModelIndex br = model->index(model->rowCount(root) - 1,
3140 qMax(columnSectionAnchor, column), root);
3141 if (horizontalHeader->sectionsMoved() && tl.column() != br.column())
3142 q->setSelection(q->visualRect(tl)|q->visualRect(br), command);
3143 else
3144 selectionModel->select(QItemSelection(tl, br), command);
3145 }
3146}
3147
3148/*!
3149 \reimp
3150 */
3151void QTableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3152{
3153#ifndef QT_NO_ACCESSIBILITY
3154 if (QAccessible::isActive()) {
3155 if (current.isValid()) {
3156 int entry = visualIndex(current) + 1;
3157 if (horizontalHeader())
3158 ++entry;
3159 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Focus);
3160 }
3161 }
3162#endif
3163 QAbstractItemView::currentChanged(current, previous);
3164}
3165
3166/*!
3167 \reimp
3168 */
3169void QTableView::selectionChanged(const QItemSelection &selected,
3170 const QItemSelection &deselected)
3171{
3172#ifndef QT_NO_ACCESSIBILITY
3173 if (QAccessible::isActive()) {
3174 // ### does not work properly for selection ranges.
3175 QModelIndex sel = selected.indexes().value(0);
3176 if (sel.isValid()) {
3177 int entry = visualIndex(sel);
3178 if (horizontalHeader())
3179 ++entry;
3180 QAccessible::updateAccessibility(viewport(), entry, QAccessible::Selection);
3181 }
3182 QModelIndex desel = deselected.indexes().value(0);
3183 if (desel.isValid()) {
3184 int entry = visualIndex(sel);
3185 if (horizontalHeader())
3186 ++entry;
3187 QAccessible::updateAccessibility(viewport(), entry, QAccessible::SelectionRemove);
3188 }
3189 }
3190#endif
3191 QAbstractItemView::selectionChanged(selected, deselected);
3192}
3193
3194int QTableView::visualIndex(const QModelIndex &index) const
3195{
3196 return index.row();
3197}
3198
3199QT_END_NAMESPACE
3200
3201#include "qtableview.moc"
3202
3203#include "moc_qtableview.cpp"
3204
3205#endif // QT_NO_TABLEVIEW
Note: See TracBrowser for help on using the repository browser.