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

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

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

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