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

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

trunk: Merged in qt 4.6.2 sources.

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