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

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

trunk: Merged in qt 4.6.1 sources.

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