Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtableview.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtableview.h"
5
6#include <qheaderview.h>
8#include <qapplication.h>
9#include <qpainter.h>
10#include <qstyle.h>
11#include <qsize.h>
12#include <qevent.h>
13#include <qbitarray.h>
14#include <qscrollbar.h>
15#if QT_CONFIG(abstractbutton)
16#include <qabstractbutton.h>
17#endif
18#include <private/qapplication_p.h>
19#include <private/qtableview_p.h>
20#include <private/qheaderview_p.h>
21#include <private/qscrollbar_p.h>
22#if QT_CONFIG(accessibility)
23#include <qaccessible.h>
24#endif
25
26#include <algorithm>
27
29
34{
35 spans.push_back(span);
36 Index::iterator it_y = index.lowerBound(-span->top());
37 if (it_y == index.end() || it_y.key() != -span->top()) {
38 //there is no spans that starts with the row in the index, so create a sublist for it.
39 SubIndex sub_index;
40 if (it_y != index.end()) {
41 //the previouslist is the list of spans that sarts _before_ the row of the span.
42 // and which may intersect this row.
43 const SubIndex previousList = it_y.value();
44 for (Span *s : previousList) {
45 //If a subspans intersect the row, we need to split it into subspans
46 if (s->bottom() >= span->top())
47 sub_index.insert(-s->left(), s);
48 }
49 }
50 it_y = index.insert(-span->top(), sub_index);
51 //we will insert span to *it_y in the later loop
52 }
53
54 //insert the span as supspan in all the lists that intesects the span
55 while(-it_y.key() <= span->bottom()) {
56 (*it_y).insert(-span->left(), span);
57 if (it_y == index.begin())
58 break;
59 --it_y;
60 }
61}
62
63
72{
73 if (old_height < span->height()) {
74 //add the span as subspan in all the lists that intersect the new covered columns
75 Index::iterator it_y = index.lowerBound(-(span->top() + old_height - 1));
76 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
77 while (-it_y.key() <= span->bottom()) {
78 (*it_y).insert(-span->left(), span);
79 if (it_y == index.begin())
80 break;
81 --it_y;
82 }
83 } else if (old_height > span->height()) {
84 //remove the span from all the subspans lists that intersect the columns not covered anymore
85 Index::iterator it_y = index.lowerBound(-qMax(span->bottom(), span->top())); //qMax useful if height is 0
86 Q_ASSERT(it_y != index.end()); //it_y must exist since the span is in the list
87 while (-it_y.key() <= span->top() + old_height -1) {
88 if (-it_y.key() > span->bottom()) {
89 int removed = (*it_y).remove(-span->left());
90 Q_ASSERT(removed == 1);
91 Q_UNUSED(removed);
92 if (it_y->isEmpty()) {
93 it_y = index.erase(it_y);
94 }
95 }
96 if (it_y == index.begin())
97 break;
98 --it_y;
99 }
100 }
101
102 if (span->width() == 0 && span->height() == 0) {
103 spans.remove(span);
104 delete span;
105 }
106}
107
113{
114 Index::const_iterator it_y = index.lowerBound(-y);
115 if (it_y == index.end())
116 return nullptr;
117 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
118 if (it_x == (*it_y).end())
119 return nullptr;
120 Span *span = *it_x;
121 if (span->right() >= x && span->bottom() >= y)
122 return span;
123 return nullptr;
124}
125
126
131{
133 index.clear();
134 spans.clear();
135}
136
140QSet<QSpanCollection::Span *> QSpanCollection::spansInRect(int x, int y, int w, int h) const
141{
142 QSet<Span *> list;
143 Index::const_iterator it_y = index.lowerBound(-y);
144 if (it_y == index.end())
145 --it_y;
146 while(-it_y.key() <= y + h) {
147 SubIndex::const_iterator it_x = (*it_y).lowerBound(-x);
148 if (it_x == (*it_y).end())
149 --it_x;
150 while(-it_x.key() <= x + w) {
151 Span *s = *it_x;
152 if (s->bottom() >= y && s->right() >= x)
153 list << s;
154 if (it_x == (*it_y).begin())
155 break;
156 --it_x;
157 }
158 if (it_y == index.begin())
159 break;
160 --it_y;
161 }
162 return list;
163}
164
165#undef DEBUG_SPAN_UPDATE
166
167#ifdef DEBUG_SPAN_UPDATE
169{
170 str << '(' << span.top() << ',' << span.left() << ',' << span.bottom() << ',' << span.right() << ')';
171 return str;
172}
173
175{
176 for (const auto *span : spans)
177 debug << span << *span;
178 return debug;
179}
180#endif
181
186{
187#ifdef DEBUG_SPAN_UPDATE
188 qDebug() << start << end << Qt::endl << index;
189#endif
190 if (spans.empty())
191 return;
192
193 int delta = end - start + 1;
194#ifdef DEBUG_SPAN_UPDATE
195 qDebug("Before");
196#endif
197 for (Span *span : spans) {
198#ifdef DEBUG_SPAN_UPDATE
199 qDebug() << span << *span;
200#endif
201 if (span->m_bottom < start)
202 continue;
203 if (span->m_top >= start)
204 span->m_top += delta;
205 span->m_bottom += delta;
206 }
207
208#ifdef DEBUG_SPAN_UPDATE
209 qDebug("After");
210 qDebug() << spans;
211#endif
212
213 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
214 int y = -it_y.key();
215 if (y < start) {
216 ++it_y;
217 continue;
218 }
219
220 index.insert(-y - delta, it_y.value());
221 it_y = index.erase(it_y);
222 }
223#ifdef DEBUG_SPAN_UPDATE
224 qDebug() << index;
225#endif
226}
227
232{
233#ifdef DEBUG_SPAN_UPDATE
234 qDebug() << start << end << Qt::endl << index;
235#endif
236 if (spans.empty())
237 return;
238
239 int delta = end - start + 1;
240#ifdef DEBUG_SPAN_UPDATE
241 qDebug("Before");
242#endif
243 for (Span *span : spans) {
244#ifdef DEBUG_SPAN_UPDATE
245 qDebug() << span << *span;
246#endif
247 if (span->m_right < start)
248 continue;
249 if (span->m_left >= start)
250 span->m_left += delta;
251 span->m_right += delta;
252 }
253
254#ifdef DEBUG_SPAN_UPDATE
255 qDebug("After");
256 qDebug() << spans;
257#endif
258
259 for (Index::iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
260 SubIndex &subindex = it_y.value();
261 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
262 int x = -it.key();
263 if (x < start) {
264 ++it;
265 continue;
266 }
267 subindex.insert(-x - delta, it.value());
268 it = subindex.erase(it);
269 }
270 }
271#ifdef DEBUG_SPAN_UPDATE
272 qDebug() << index;
273#endif
274}
275
281bool QSpanCollection::cleanSpanSubIndex(QSpanCollection::SubIndex &subindex, int y, bool update)
282{
283 if (subindex.isEmpty())
284 return true;
285
286 bool should_be_deleted = true;
287 SubIndex::iterator it = subindex.end();
288 do {
289 --it;
290 int x = -it.key();
291 Span *span = it.value();
292 if (span->will_be_deleted) {
293 it = subindex.erase(it);
294 continue;
295 }
296 if (update && span->m_left != x) {
297 subindex.insert(-span->m_left, span);
298 it = subindex.erase(it);
299 }
300 if (should_be_deleted && span->m_top == y)
301 should_be_deleted = false;
302 } while (it != subindex.begin());
303
304 return should_be_deleted;
305}
306
311{
312#ifdef DEBUG_SPAN_UPDATE
313 qDebug() << start << end << Qt::endl << index;
314#endif
315 if (spans.empty())
316 return;
317
318 SpanList spansToBeDeleted;
319 int delta = end - start + 1;
320#ifdef DEBUG_SPAN_UPDATE
321 qDebug("Before");
322#endif
323 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
324 Span *span = *it;
325#ifdef DEBUG_SPAN_UPDATE
326 qDebug() << span << *span;
327#endif
328 if (span->m_bottom < start) {
329 ++it;
330 continue;
331 }
332 if (span->m_top < start) {
333 if (span->m_bottom <= end)
334 span->m_bottom = start - 1;
335 else
336 span->m_bottom -= delta;
337 } else {
338 if (span->m_bottom > end) {
339 if (span->m_top <= end)
340 span->m_top = start;
341 else
342 span->m_top -= delta;
343 span->m_bottom -= delta;
344 } else {
345 span->will_be_deleted = true;
346 }
347 }
348 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
349 span->will_be_deleted = true;
350 if (span->will_be_deleted) {
351 spansToBeDeleted.push_back(span);
352 it = spans.erase(it);
353 } else {
354 ++it;
355 }
356 }
357
358#ifdef DEBUG_SPAN_UPDATE
359 qDebug("After");
360 qDebug() << spans;
361#endif
362 if (spans.empty()) {
363 qDeleteAll(spansToBeDeleted);
364 index.clear();
365 return;
366 }
367
368 Index::iterator it_y = index.end();
369 do {
370 --it_y;
371 int y = -it_y.key();
372 SubIndex &subindex = it_y.value();
373 if (y < start) {
374 if (cleanSpanSubIndex(subindex, y))
375 it_y = index.erase(it_y);
376 } else if (y >= start && y <= end) {
377 bool span_at_start = false;
378 SubIndex spansToBeMoved;
379 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ++it) {
380 Span *span = it.value();
381 if (span->will_be_deleted)
382 continue;
383 if (!span_at_start && span->m_top == start)
384 span_at_start = true;
385 spansToBeMoved.insert(it.key(), span);
386 }
387
388 if (y == start && span_at_start)
389 subindex.clear();
390 else
391 it_y = index.erase(it_y);
392
393 if (span_at_start) {
394 Index::iterator it_start;
395 if (y == start)
396 it_start = it_y;
397 else {
398 it_start = index.find(-start);
399 if (it_start == index.end())
400 it_start = index.insert(-start, SubIndex());
401 }
402 SubIndex &start_subindex = it_start.value();
403 for (SubIndex::iterator it = spansToBeMoved.begin(); it != spansToBeMoved.end(); ++it)
404 start_subindex.insert(it.key(), it.value());
405 }
406 } else {
407 if (y == end + 1) {
408 Index::iterator it_top = index.find(-y + delta);
409 if (it_top == index.end())
410 it_top = index.insert(-y + delta, SubIndex());
411 for (SubIndex::iterator it = subindex.begin(); it != subindex.end(); ) {
412 Span *span = it.value();
413 if (!span->will_be_deleted)
414 it_top.value().insert(it.key(), span);
415 ++it;
416 }
417 } else {
418 index.insert(-y + delta, subindex);
419 }
420 it_y = index.erase(it_y);
421 }
422 } while (it_y != index.begin());
423
424#ifdef DEBUG_SPAN_UPDATE
425 qDebug() << index;
426 qDebug("Deleted");
427 qDebug() << spansToBeDeleted;
428#endif
429 qDeleteAll(spansToBeDeleted);
430}
431
436{
437#ifdef DEBUG_SPAN_UPDATE
438 qDebug() << start << end << Qt::endl << index;
439#endif
440 if (spans.empty())
441 return;
442
443 SpanList toBeDeleted;
444 int delta = end - start + 1;
445#ifdef DEBUG_SPAN_UPDATE
446 qDebug("Before");
447#endif
448 for (SpanList::iterator it = spans.begin(); it != spans.end(); ) {
449 Span *span = *it;
450#ifdef DEBUG_SPAN_UPDATE
451 qDebug() << span << *span;
452#endif
453 if (span->m_right < start) {
454 ++it;
455 continue;
456 }
457 if (span->m_left < start) {
458 if (span->m_right <= end)
459 span->m_right = start - 1;
460 else
461 span->m_right -= delta;
462 } else {
463 if (span->m_right > end) {
464 if (span->m_left <= end)
465 span->m_left = start;
466 else
467 span->m_left -= delta;
468 span->m_right -= delta;
469 } else {
470 span->will_be_deleted = true;
471 }
472 }
473 if (span->m_top == span->m_bottom && span->m_left == span->m_right)
474 span->will_be_deleted = true;
475 if (span->will_be_deleted) {
476 toBeDeleted.push_back(span);
477 it = spans.erase(it);
478 } else {
479 ++it;
480 }
481 }
482
483#ifdef DEBUG_SPAN_UPDATE
484 qDebug("After");
485 qDebug() << spans;
486#endif
487 if (spans.empty()) {
488 qDeleteAll(toBeDeleted);
489 index.clear();
490 return;
491 }
492
493 for (Index::iterator it_y = index.begin(); it_y != index.end(); ) {
494 int y = -it_y.key();
495 if (cleanSpanSubIndex(it_y.value(), y, true))
496 it_y = index.erase(it_y);
497 else
498 ++it_y;
499 }
500
501#ifdef DEBUG_SPAN_UPDATE
502 qDebug() << index;
503 qDebug("Deleted");
504 qDebug() << toBeDeleted;
505#endif
506 qDeleteAll(toBeDeleted);
507}
508
509#ifdef QT_BUILD_INTERNAL
514bool QSpanCollection::checkConsistency() const
515{
516 for (Index::const_iterator it_y = index.begin(); it_y != index.end(); ++it_y) {
517 int y = -it_y.key();
518 const SubIndex &subIndex = it_y.value();
519 for (SubIndex::const_iterator it = subIndex.begin(); it != subIndex.end(); ++it) {
520 int x = -it.key();
521 Span *span = it.value();
522 const bool contains = std::find(spans.begin(), spans.end(), span) != spans.end();
523 if (!contains || span->left() != x || y < span->top() || y > span->bottom())
524 return false;
525 }
526 }
527
528 for (const Span *span : spans) {
529 if (span->width() < 1 || span->height() < 1
530 || (span->width() == 1 && span->height() == 1))
531 return false;
532 for (int y = span->top(); y <= span->bottom(); ++y) {
533 Index::const_iterator it_y = index.find(-y);
534 if (it_y == index.end()) {
535 if (y == span->top())
536 return false;
537 else
538 continue;
539 }
540 const SubIndex &subIndex = it_y.value();
541 SubIndex::const_iterator it = subIndex.find(-span->left());
542 if (it == subIndex.end() || it.value() != span)
543 return false;
544 }
545 }
546 return true;
547}
548#endif
549
550#if QT_CONFIG(abstractbutton)
551class QTableCornerButton : public QAbstractButton
552{
554public:
555 QTableCornerButton(QWidget *parent) : QAbstractButton(parent) {}
556 void paintEvent(QPaintEvent*) override {
558 opt.initFrom(this);
559 QStyle::State state = QStyle::State_None;
560 if (isEnabled())
562 if (isActiveWindow())
564 if (isDown())
566 opt.state = state;
567 opt.rect = rect();
569 QPainter painter(this);
570 style()->drawControl(QStyle::CE_Header, &opt, &painter, this);
571 }
572};
573#endif
574
576{
577 Q_Q(QTableView);
578
580
581 QHeaderView *vertical = new QHeaderView(Qt::Vertical, q);
582 vertical->setSectionsClickable(true);
583 vertical->setHighlightSections(true);
584 q->setVerticalHeader(vertical);
585
586 QHeaderView *horizontal = new QHeaderView(Qt::Horizontal, q);
587 horizontal->setSectionsClickable(true);
588 horizontal->setHighlightSections(true);
589 q->setHorizontalHeader(horizontal);
590
591 tabKeyNavigation = true;
592
593#if QT_CONFIG(abstractbutton)
594 cornerWidget = new QTableCornerButton(q);
595 cornerWidget->setFocusPolicy(Qt::NoFocus);
596 cornerWidgetConnection = QObject::connect(
597 cornerWidget, &QTableCornerButton::clicked,
599#endif
600}
601
617
623{
624 Q_ASSERT(range && range->isValid());
625
626 int top = range->top();
627 int left = range->left();
628 int bottom = range->bottom();
629 int right = range->right();
630
632 --bottom;
634 --right;
635
636 if (top > bottom || left > right) { // everything is hidden
638 return;
639 }
640
642 ++top;
644 ++left;
645
646 if (top > bottom || left > right) { // everything is hidden
648 return;
649 }
650
651 QModelIndex bottomRight = model->index(bottom, right, range->parent());
652 QModelIndex topLeft = model->index(top, left, range->parent());
653 *range = QItemSelectionRange(topLeft, bottomRight);
654}
655
656QRect QTableViewPrivate::intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const
657{
658 Q_Q(const QTableView);
659
660 using minMaxPair = std::pair<int, int>;
661 const auto calcMinMax = [q](QHeaderView *hdr, int startIdx, int endIdx, minMaxPair bounds) -> minMaxPair
662 {
663 minMaxPair ret(std::numeric_limits<int>::max(), std::numeric_limits<int>::min());
664 if (hdr->sectionsMoved()) {
665 for (int i = startIdx; i <= endIdx; ++i) {
666 const int start = hdr->sectionViewportPosition(i);
667 const int end = start + hdr->sectionSize(i);
668 ret.first = std::min(start, ret.first);
669 ret.second = std::max(end, ret.second);
670 if (ret.first <= bounds.first && ret.second >= bounds.second)
671 break;
672 }
673 } else {
674 if (q->isRightToLeft() && q->horizontalHeader() == hdr)
675 std::swap(startIdx, endIdx);
676 ret.first = hdr->sectionViewportPosition(startIdx);
677 ret.second = hdr->sectionViewportPosition(endIdx) +
678 hdr->sectionSize(endIdx);
679 }
680 return ret;
681 };
682
683 const auto yVals = calcMinMax(verticalHeader, topLeft.row(), bottomRight.row(),
684 minMaxPair(rect.top(), rect.bottom()));
685 if (yVals.first == yVals.second) // all affected rows are hidden
686 return QRect();
687
688 // short circuit: check if no row is inside rect
689 const QRect colRect(QPoint(rect.left(), yVals.first),
690 QPoint(rect.right(), yVals.second));
691 const QRect intersected = rect.intersected(colRect);
692 if (intersected.isNull())
693 return QRect();
694
695 const auto xVals = calcMinMax(horizontalHeader, topLeft.column(), bottomRight.column(),
696 minMaxPair(rect.left(), rect.right()));
697 const QRect updateRect(QPoint(xVals.first, yVals.first),
698 QPoint(xVals.second, yVals.second));
699 return rect.intersected(updateRect);
700}
701
706void QTableViewPrivate::setSpan(int row, int column, int rowSpan, int columnSpan)
707{
708 if (Q_UNLIKELY(row < 0 || column < 0 || rowSpan <= 0 || columnSpan <= 0)) {
709 qWarning("QTableView::setSpan: invalid span given: (%d, %d, %d, %d)",
711 return;
712 }
714 if (sp) {
715 if (sp->top() != row || sp->left() != column) {
716 qWarning("QTableView::setSpan: span cannot overlap");
717 return;
718 }
719 if (rowSpan == 1 && columnSpan == 1) {
720 rowSpan = columnSpan = 0;
721 }
722 const int old_height = sp->height();
723 sp->m_bottom = row + rowSpan - 1;
724 sp->m_right = column + columnSpan - 1;
725 spans.updateSpan(sp, old_height);
726 return;
727 } else if (Q_UNLIKELY(rowSpan == 1 && columnSpan == 1)) {
728 qWarning("QTableView::setSpan: single cell span won't be added");
729 return;
730 }
733}
734
740{
742 if (sp)
743 return *sp;
744
745 return QSpanCollection::Span(row, column, 1, 1);
746}
747
753{
754 int visual = header->visualIndex(logical);
755 for (int i = 1; i < span; ) {
756 if (++visual >= header->count())
757 break;
758 logical = header->logicalIndex(visual);
759 ++i;
760 }
761 return logical;
762}
763
770{
771 int endLogical = sectionSpanEndLogical(header, logical, span);
772 return header->sectionPosition(endLogical)
773 - header->sectionPosition(logical)
774 + header->sectionSize(endLogical);
775}
776
783bool QTableViewPrivate::spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
784{
785 if (logical == spanLogical)
786 return true; // it's the start of the span
787 int visual = header->visualIndex(spanLogical);
788 for (int i = 1; i < span; ) {
789 if (++visual >= header->count())
790 break;
791 spanLogical = header->logicalIndex(visual);
792 if (logical == spanLogical)
793 return true;
794 ++i;
795 }
796 return false;
797}
798
805 SearchDirection searchDirection) const
806{
807 const int lc = logicalColumn(column);
808 int visualRow = rowToStart;
809 const auto isCellActive = [this](int vr, int lc)
810 {
811 const int lr = logicalRow(vr);
812 return !isRowHidden(lr) && isCellEnabled(lr, lc);
813 };
814 switch (searchDirection) {
816 if (visualRow < limit) {
817 while (!isCellActive(visualRow, lc)) {
818 if (++visualRow == limit)
819 return rowToStart;
820 }
821 }
822 break;
824 while (visualRow > limit && !isCellActive(visualRow, lc))
825 --visualRow;
826 break;
827 }
828 return visualRow;
829}
830
837 SearchDirection searchDirection) const
838{
839 const int lr = logicalRow(row);
840 int visualColumn = columnToStart;
841 const auto isCellActive = [this](int lr, int vc)
842 {
843 const int lc = logicalColumn(vc);
844 return !isColumnHidden(lc) && isCellEnabled(lr, lc);
845 };
846 switch (searchDirection) {
848 while (visualColumn < limit && !isCellActive(lr, visualColumn))
849 ++visualColumn;
850 break;
852 while (visualColumn > limit && !isCellActive(lr, visualColumn))
853 --visualColumn;
854 break;
855 }
856 return visualColumn;
857}
858
864{
865 Q_Q(const QTableView);
866 // vertical
867 int row = span.top();
869 int rowh = rowSpanHeight(row, span.height());
870 // horizontal
871 int column = span.left();
872 int colw = columnSpanWidth(column, span.width());
873 if (q->isRightToLeft())
874 column = span.right();
876
877 const int i = showGrid ? 1 : 0;
878 if (q->isRightToLeft())
879 return QRect(colp + i, rowp, colw - i, rowh - i);
880 return QRect(colp, rowp, colw - i, rowh - i);
881}
882
890 const QStyleOptionViewItem &option, QBitArray *drawn,
891 int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
892{
893 Q_Q(const QTableView);
894 bool alternateBase = false;
895 QRegion region = viewport->rect();
896
897 QSet<QSpanCollection::Span *> visibleSpans;
898 bool sectionMoved = verticalHeader->sectionsMoved() || horizontalHeader->sectionsMoved();
899
900 if (!sectionMoved) {
901 visibleSpans = spans.spansInRect(logicalColumn(firstVisualColumn), logicalRow(firstVisualRow),
902 lastVisualColumn - firstVisualColumn + 1, lastVisualRow - firstVisualRow + 1);
903 } else {
904 // Any cell outside the viewport, on the top or left, can still end up visible inside the
905 // viewport if is has a span. Calculating if a spanned cell overlaps with the viewport is
906 // "easy" enough when the columns (or rows) in the view are aligned with the columns
907 // in the model; In that case you know that if a column is outside the viewport on the
908 // right, it cannot affect the drawing of the cells inside the viewport, even with a span.
909 // And under that assumption, the spansInRect() function can be used (which is optimized
910 // to only iterate the spans that are close to the viewport).
911 // But when the view has rearranged the columns (or rows), this is no longer true. In that
912 // case, even if a column, according to the model, is outside the viewport on the right, it
913 // can still overlap with the viewport. This can happen if it was moved to the left of the
914 // viewport and one of its cells has a span. In that case we need to take the theoretically
915 // slower route and iterate through all the spans, and check if any of them overlaps with
916 // the viewport.
917 const auto spanList = spans.spans;
918 for (QSpanCollection::Span *span : spanList) {
919 const int spanVisualLeft = visualColumn(span->left());
920 const int spanVisualTop = visualRow(span->top());
921 const int spanVisualRight = spanVisualLeft + span->width() - 1;
922 const int spanVisualBottom = spanVisualTop + span->height() - 1;
923 if ((spanVisualLeft <= lastVisualColumn && spanVisualRight >= firstVisualColumn)
924 && (spanVisualTop <= lastVisualRow && spanVisualBottom >= firstVisualRow))
925 visibleSpans.insert(span);
926 }
927 }
928
929 for (QSpanCollection::Span *span : std::as_const(visibleSpans)) {
930 int row = span->top();
931 int col = span->left();
933 if (!index.isValid())
934 continue;
936 rect.translate(scrollDelayOffset);
937 if (!area.intersects(rect))
938 continue;
939 QStyleOptionViewItem opt = option;
940 opt.rect = rect;
941 alternateBase = alternatingColors && (span->top() & 1);
942 opt.features.setFlag(QStyleOptionViewItem::Alternate, alternateBase);
944 if (showGrid) {
945 // adjust the clip rect to be able to paint the top & left grid lines
946 // if the headers are not visible, see paintEvent()
948 rect.setTop(rect.top() + 1);
949 if (verticalHeader->visualIndex(row) == 0) {
950 if (q->isLeftToRight())
951 rect.setLeft(rect.left() + 1);
952 else
953 rect.setRight(rect.right() - 1);
954 }
955 }
956 region -= rect;
957 for (int r = span->top(); r <= span->bottom(); ++r) {
958 const int vr = visualRow(r);
959 if (vr < firstVisualRow || vr > lastVisualRow)
960 continue;
961 for (int c = span->left(); c <= span->right(); ++c) {
962 const int vc = visualColumn(c);
963 if (vc < firstVisualColumn || vc > lastVisualColumn)
964 continue;
965 drawn->setBit((vr - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
966 + vc - firstVisualColumn);
967 }
968 }
969
970 }
971 painter->setClipRegion(region);
972}
973
979{
980 Q_UNUSED(parent);
982}
983
993
999{
1000 Q_UNUSED(parent);
1002}
1003
1009{
1010 Q_UNUSED(parent);
1012}
1013
1022
1027void QTableViewPrivate::drawCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
1028{
1029 Q_Q(QTableView);
1030 QStyleOptionViewItem opt = option;
1031
1034 if (index == hover)
1036 if (option.state & QStyle::State_Enabled) {
1038 if ((model->flags(index) & Qt::ItemIsEnabled) == 0) {
1039 opt.state &= ~QStyle::State_Enabled;
1040 cg = QPalette::Disabled;
1041 } else {
1042 cg = QPalette::Normal;
1043 }
1045 }
1046
1047 if (index == q->currentIndex()) {
1048 const bool focus = (q->hasFocus() || viewport->hasFocus()) && q->currentIndex().isValid();
1049 if (focus)
1051 }
1052
1053 q->style()->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, q);
1054
1055 q->itemDelegateForIndex(index)->paint(painter, opt, index);
1056}
1057
1062int QTableViewPrivate::widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const
1063{
1064 Q_Q(const QTableView);
1065 const int oldHint = hint;
1067 if (editor && persistent.contains(editor)) {
1068 hint = qMax(hint, editor->sizeHint().width());
1069 int min = editor->minimumSize().width();
1070 int max = editor->maximumSize().width();
1071 hint = qBound(min, hint, max);
1072 }
1073 hint = qMax(hint, q->itemDelegateForIndex(index)->sizeHint(option, index).width());
1074
1075 if (hasSpans()) {
1076 auto span = spans.spanAt(index.column(), index.row());
1077 if (span && span->m_left == index.column() && span->m_top == index.row()) {
1078 // spans are screwed up when sections are moved
1079 const auto left = logicalColumn(span->m_left);
1080 for (int i = 1; i <= span->width(); ++i)
1081 hint -= q->columnWidth(visualColumn(left + i));
1082 }
1083 hint = std::max(hint, oldHint);
1084 }
1085 return hint;
1086}
1087
1092int QTableViewPrivate::heightHintForIndex(const QModelIndex &index, int hint, QStyleOptionViewItem &option) const
1093{
1094 Q_Q(const QTableView);
1096 if (editor && persistent.contains(editor)) {
1098 int min = editor->minimumSize().height();
1099 int max = editor->maximumSize().height();
1100 hint = qBound(min, hint, max);
1101 }
1102
1103 if (wrapItemText) {// for wrapping boundaries
1104 option.rect.setY(q->rowViewportPosition(index.row()));
1105 int height = q->rowHeight(index.row());
1106 // if the option.height == 0 then q->itemDelegateForIndex(index)->sizeHint(option, index) will be wrong.
1107 // The option.height == 0 is used to conclude that the text is not wrapped, and hence it will
1108 // (exactly like widthHintForIndex) return a QSize with a long width (that we don't use) -
1109 // and the height of the text if it was/is on one line.
1110 // What we want is a height hint for the current width (and we know that this section is not hidden)
1111 // Therefore we catch this special situation with:
1112 if (height == 0)
1113 height = 1;
1114 option.rect.setHeight(height);
1115 option.rect.setX(q->columnViewportPosition(index.column()));
1116 option.rect.setWidth(q->columnWidth(index.column()));
1117 if (hasSpans()) {
1118 auto span = spans.spanAt(index.column(), index.row());
1119 if (span && span->m_left == index.column() && span->m_top == index.row())
1120 option.rect.setWidth(std::max(option.rect.width(), visualSpanRect(*span).width()));
1121 }
1122 // 1px less space when grid is shown (see drawCell)
1123 if (showGrid)
1124 option.rect.setWidth(option.rect.width() - 1);
1125 }
1126 hint = qMax(hint, q->itemDelegateForIndex(index)->sizeHint(option, index).height());
1127 return hint;
1128}
1129
1130
1220 : QAbstractItemView(*new QTableViewPrivate, parent)
1221{
1222 Q_D(QTableView);
1223 d->init();
1224}
1225
1230 : QAbstractItemView(dd, parent)
1231{
1232 Q_D(QTableView);
1233 d->init();
1234}
1235
1240{
1241 Q_D(QTableView);
1242 d->clearConnections();
1243}
1244
1249{
1250 Q_D(const QTableView);
1251 QSize result( (d->verticalHeader->isHidden() ? 0 : d->verticalHeader->width()) + d->horizontalHeader->length(),
1252 (d->horizontalHeader->isHidden() ? 0 : d->horizontalHeader->height()) + d->verticalHeader->length());
1253 return result;
1254}
1255
1260{
1261 Q_D(QTableView);
1262 if (model == d->model)
1263 return;
1264 //let's disconnect from the old model
1265 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
1266 for (const QMetaObject::Connection &connection : d->modelConnections)
1268 }
1269 if (d->selectionModel) { // support row editing
1270 disconnect(d->selectionmodelConnection);
1271 }
1272 if (model) { //and connect to the new one
1273 d->modelConnections = {
1282 };
1283 }
1284 d->verticalHeader->setModel(model);
1285 d->horizontalHeader->setModel(model);
1287}
1288
1293{
1294 Q_D(QTableView);
1295 if (index == d->root) {
1296 viewport()->update();
1297 return;
1298 }
1299 d->verticalHeader->setRootIndex(index);
1300 d->horizontalHeader->setRootIndex(index);
1302}
1303
1308{
1309 Q_D(QTableView);
1311 if (!d->verticalHeader->updatesEnabled())
1312 d->verticalHeader->setUpdatesEnabled(true);
1313}
1314
1319{
1320 Q_D(QTableView);
1322 if (d->selectionModel) {
1323 // support row editing
1324 disconnect(d->selectionmodelConnection);
1325 }
1326
1327 d->verticalHeader->setSelectionModel(selectionModel);
1328 d->horizontalHeader->setSelectionModel(selectionModel);
1330
1331 if (d->selectionModel) {
1332 // support row editing
1333 d->selectionmodelConnection =
1335 d->model, &QAbstractItemModel::submit);
1336 }
1337}
1338
1345{
1346 Q_D(const QTableView);
1347 return d->horizontalHeader;
1348}
1349
1356{
1357 Q_D(const QTableView);
1358 return d->verticalHeader;
1359}
1360
1367{
1368 Q_D(QTableView);
1369
1370 if (!header || header == d->horizontalHeader)
1371 return;
1372 for (const QMetaObject::Connection &connection : d->horHeaderConnections)
1374 if (d->horizontalHeader && d->horizontalHeader->parent() == this)
1375 delete d->horizontalHeader;
1376 d->horizontalHeader = header;
1377 d->horizontalHeader->setParent(this);
1378 d->horizontalHeader->setFirstSectionMovable(true);
1379 if (!d->horizontalHeader->model()) {
1380 d->horizontalHeader->setModel(d->model);
1381 if (d->selectionModel)
1382 d->horizontalHeader->setSelectionModel(d->selectionModel);
1383 }
1384
1385 d->horHeaderConnections = {
1386 connect(d->horizontalHeader,&QHeaderView::sectionResized,
1388 connect(d->horizontalHeader, &QHeaderView::sectionMoved,
1390 connect(d->horizontalHeader, &QHeaderView::sectionCountChanged,
1394 connect(d->horizontalHeader, &QHeaderView::geometriesChanged,
1396 };
1397 //update the sorting enabled states on the new header
1398 setSortingEnabled(d->sortingEnabled);
1399}
1400
1407{
1408 Q_D(QTableView);
1409
1410 if (!header || header == d->verticalHeader)
1411 return;
1412 for (const QMetaObject::Connection &connection : d->verHeaderConnections)
1414 if (d->verticalHeader && d->verticalHeader->parent() == this)
1415 delete d->verticalHeader;
1416 d->verticalHeader = header;
1417 d->verticalHeader->setParent(this);
1418 d->verticalHeader->setFirstSectionMovable(true);
1419 if (!d->verticalHeader->model()) {
1420 d->verticalHeader->setModel(d->model);
1421 if (d->selectionModel)
1422 d->verticalHeader->setSelectionModel(d->selectionModel);
1423 }
1424
1425 d->verHeaderConnections = {
1426 connect(d->verticalHeader, &QHeaderView::sectionResized,
1427 this, &QTableView::rowResized),
1428 connect(d->verticalHeader, &QHeaderView::sectionMoved,
1429 this, &QTableView::rowMoved),
1430 connect(d->verticalHeader, &QHeaderView::sectionCountChanged,
1432 connect(d->verticalHeader, &QHeaderView::sectionPressed,
1433 this, &QTableView::selectRow),
1436 connect(d->verticalHeader, &QHeaderView::geometriesChanged,
1438 connect(d->verticalHeader, &QHeaderView::sectionEntered,
1439 this, [d](int row) { d->selectRow(row, false); })
1440 };
1441}
1442
1449{
1450 Q_D(QTableView);
1451
1452 d->delayedAutoScroll.stop(); // auto scroll was canceled by the user scrolling
1453
1454 dx = isRightToLeft() ? -dx : dx;
1455 if (dx) {
1456 int oldOffset = d->horizontalHeader->offset();
1457 d->horizontalHeader->d_func()->setScrollOffset(horizontalScrollBar(), horizontalScrollMode());
1459 int newOffset = d->horizontalHeader->offset();
1460 dx = isRightToLeft() ? newOffset - oldOffset : oldOffset - newOffset;
1461 }
1462 }
1463 if (dy) {
1464 int oldOffset = d->verticalHeader->offset();
1465 d->verticalHeader->d_func()->setScrollOffset(verticalScrollBar(), verticalScrollMode());
1467 int newOffset = d->verticalHeader->offset();
1468 dy = oldOffset - newOffset;
1469 }
1470 }
1471 d->scrollContentsBy(dx, dy);
1472
1473 if (d->showGrid) {
1474 //we need to update the first line of the previous top item in the view
1475 //because it has the grid drawn if the header is invisible.
1476 //It is strictly related to what's done at then end of the paintEvent
1477 if (dy > 0 && d->horizontalHeader->isHidden()) {
1478 d->viewport->update(0, dy, d->viewport->width(), dy);
1479 }
1480 if (dx > 0 && d->verticalHeader->isHidden()) {
1481 d->viewport->update(dx, 0, dx, d->viewport->height());
1482 }
1483 }
1484}
1485
1489void QTableView::initViewItemOption(QStyleOptionViewItem *option) const
1490{
1492 option->showDecorationSelected = true;
1493}
1494
1499{
1500 Q_D(QTableView);
1501 // setup temp variables for the painting
1502 QStyleOptionViewItem option;
1504 const QPoint offset = d->scrollDelayOffset;
1505 const bool showGrid = d->showGrid;
1506 const int gridSize = showGrid ? 1 : 0;
1507 const int gridHint = style()->styleHint(QStyle::SH_Table_GridLineColor, &option, this);
1508 const QColor gridColor = QColor::fromRgba(static_cast<QRgb>(gridHint));
1509 const QPen gridPen = QPen(gridColor, 1, d->gridStyle);
1510 const QHeaderView *verticalHeader = d->verticalHeader;
1511 const QHeaderView *horizontalHeader = d->horizontalHeader;
1512 const bool alternate = d->alternatingColors;
1513 const bool rightToLeft = isRightToLeft();
1514
1515 QPainter painter(d->viewport);
1516
1517 // if there's nothing to do, clear the area and return
1518 if (horizontalHeader->count() == 0 || verticalHeader->count() == 0 || !d->itemDelegate)
1519 return;
1520
1521 const int x = horizontalHeader->length() - horizontalHeader->offset() - (rightToLeft ? 0 : 1);
1522 const int y = verticalHeader->length() - verticalHeader->offset() - 1;
1523
1524 //firstVisualRow is the visual index of the first visible row. lastVisualRow is the visual index of the last visible Row.
1525 //same goes for ...VisualColumn
1526 int firstVisualRow = qMax(verticalHeader->visualIndexAt(0),0);
1527 int lastVisualRow = verticalHeader->visualIndexAt(verticalHeader->height());
1528 if (lastVisualRow == -1)
1529 lastVisualRow = d->model->rowCount(d->root) - 1;
1530
1531 int firstVisualColumn = horizontalHeader->visualIndexAt(0);
1532 int lastVisualColumn = horizontalHeader->visualIndexAt(horizontalHeader->width());
1533 if (rightToLeft)
1534 qSwap(firstVisualColumn, lastVisualColumn);
1535 if (firstVisualColumn == -1)
1536 firstVisualColumn = 0;
1537 if (lastVisualColumn == -1)
1538 lastVisualColumn = horizontalHeader->count() - 1;
1539
1540 QBitArray drawn((lastVisualRow - firstVisualRow + 1) * (lastVisualColumn - firstVisualColumn + 1));
1541
1542 const QRegion region = event->region().translated(offset);
1543
1544 if (d->hasSpans()) {
1545 d->drawAndClipSpans(region, &painter, option, &drawn,
1546 firstVisualRow, lastVisualRow, firstVisualColumn, lastVisualColumn);
1547 }
1548
1549 for (QRect dirtyArea : region) {
1550 dirtyArea.setBottom(qMin(dirtyArea.bottom(), int(y)));
1551 if (rightToLeft) {
1552 dirtyArea.setLeft(qMax(dirtyArea.left(), d->viewport->width() - int(x)));
1553 } else {
1554 dirtyArea.setRight(qMin(dirtyArea.right(), int(x)));
1555 }
1556 // dirtyArea may be invalid when the horizontal header is not stretched
1557 if (!dirtyArea.isValid())
1558 continue;
1559
1560 // get the horizontal start and end visual sections
1561 int left = horizontalHeader->visualIndexAt(dirtyArea.left());
1562 int right = horizontalHeader->visualIndexAt(dirtyArea.right());
1563 if (rightToLeft)
1564 qSwap(left, right);
1565 if (left == -1) left = 0;
1566 if (right == -1) right = horizontalHeader->count() - 1;
1567
1568 // get the vertical start and end visual sections and if alternate color
1569 int bottom = verticalHeader->visualIndexAt(dirtyArea.bottom());
1570 if (bottom == -1) bottom = verticalHeader->count() - 1;
1571 int top = 0;
1572 bool alternateBase = false;
1573 if (alternate && verticalHeader->sectionsHidden()) {
1574 const int verticalOffset = verticalHeader->offset();
1576 for (int y = 0;
1578 ++top) {
1580 if (alternate && !verticalHeader->isSectionHidden(row))
1581 alternateBase = !alternateBase;
1582 }
1583 } else {
1584 top = verticalHeader->visualIndexAt(dirtyArea.top());
1585 alternateBase = (top & 1) && alternate;
1586 }
1587 if (top == -1 || top > bottom)
1588 continue;
1589
1590 // Paint each row item
1591 for (int visualRowIndex = top; visualRowIndex <= bottom; ++visualRowIndex) {
1592 int row = verticalHeader->logicalIndex(visualRowIndex);
1594 continue;
1595 int rowY = rowViewportPosition(row);
1596 rowY += offset.y();
1597 int rowh = rowHeight(row) - gridSize;
1598
1599 // Paint each column item
1600 for (int visualColumnIndex = left; visualColumnIndex <= right; ++visualColumnIndex) {
1601 int currentBit = (visualRowIndex - firstVisualRow) * (lastVisualColumn - firstVisualColumn + 1)
1602 + visualColumnIndex - firstVisualColumn;
1603
1604 if (currentBit < 0 || currentBit >= drawn.size() || drawn.testBit(currentBit))
1605 continue;
1606 drawn.setBit(currentBit);
1607
1608 int col = horizontalHeader->logicalIndex(visualColumnIndex);
1610 continue;
1611 int colp = columnViewportPosition(col);
1612 colp += offset.x();
1613 int colw = columnWidth(col) - gridSize;
1614
1615 const QModelIndex index = d->model->index(row, col, d->root);
1616 if (index.isValid()) {
1617 option.rect = QRect(colp + (showGrid && rightToLeft ? 1 : 0), rowY, colw, rowh);
1618 if (alternate) {
1619 if (alternateBase)
1620 option.features |= QStyleOptionViewItem::Alternate;
1621 else
1622 option.features &= ~QStyleOptionViewItem::Alternate;
1623 }
1624 d->drawCell(&painter, option, index);
1625 }
1626 }
1627 alternateBase = !alternateBase && alternate;
1628 }
1629
1630 if (showGrid) {
1631 // Find the bottom right (the last rows/columns might be hidden)
1633 QPen old = painter.pen();
1634 painter.setPen(gridPen);
1635 // Paint each row
1636 for (int visualIndex = top; visualIndex <= bottom; ++visualIndex) {
1637 int row = verticalHeader->logicalIndex(visualIndex);
1639 continue;
1640 int rowY = rowViewportPosition(row);
1641 rowY += offset.y();
1642 int rowh = rowHeight(row) - gridSize;
1643 QLineF line(dirtyArea.left(), rowY + rowh, dirtyArea.right(), rowY + rowh);
1644 painter.drawLine(line.translated(0.5, 0.5));
1645 }
1646
1647 // Paint each column
1648 for (int h = left; h <= right; ++h) {
1649 int col = horizontalHeader->logicalIndex(h);
1651 continue;
1652 int colp = columnViewportPosition(col);
1653 colp += offset.x();
1654 if (!rightToLeft)
1655 colp += columnWidth(col) - gridSize;
1656 QLineF line(colp, dirtyArea.top(), colp, dirtyArea.bottom());
1657 painter.drawLine(line.translated(0.5, 0.5));
1658 }
1659 const bool drawWhenHidden = style()->styleHint(QStyle::SH_Table_AlwaysDrawLeftTopGridLines,
1660 &option, this);
1661 if (drawWhenHidden && horizontalHeader->isHidden()) {
1662 const int row = verticalHeader->logicalIndex(top);
1664 const int rowY = rowViewportPosition(row) + offset.y();
1665 if (rowY == dirtyArea.top())
1666 painter.drawLine(dirtyArea.left(), rowY, dirtyArea.right(), rowY);
1667 }
1668 }
1669 if (drawWhenHidden && verticalHeader->isHidden()) {
1670 const int col = horizontalHeader->logicalIndex(left);
1671 if (!horizontalHeader->isSectionHidden(col)) {
1672 int colX = columnViewportPosition(col) + offset.x();
1673 if (!isLeftToRight())
1674 colX += columnWidth(left) - 1;
1675 if (isLeftToRight() && colX == dirtyArea.left())
1676 painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom());
1677 if (!isLeftToRight() && colX == dirtyArea.right())
1678 painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom());
1679 }
1680 }
1681 painter.setPen(old);
1682 }
1683 }
1684
1685#if QT_CONFIG(draganddrop)
1686 // Paint the dropIndicator
1687 d->paintDropIndicator(&painter);
1688#endif
1689}
1690
1696{
1697 Q_D(const QTableView);
1698 d->executePostedLayout();
1699 int r = rowAt(pos.y());
1700 int c = columnAt(pos.x());
1701 if (r >= 0 && c >= 0) {
1702 if (d->hasSpans()) {
1703 QSpanCollection::Span span = d->span(r, c);
1704 r = span.top();
1705 c = span.left();
1706 }
1707 return d->model->index(r, c, d->root);
1708 }
1709 return QModelIndex();
1710}
1711
1721{
1722 Q_D(const QTableView);
1723 return d->horizontalHeader->offset();
1724}
1725
1735{
1736 Q_D(const QTableView);
1737 return d->verticalHeader->offset();
1738}
1739
1748QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1749{
1750 Q_D(QTableView);
1752
1753 int bottom = d->model->rowCount(d->root) - 1;
1754 // make sure that bottom is the bottommost *visible* row
1755 while (bottom >= 0 && isRowHidden(d->logicalRow(bottom)))
1756 --bottom;
1757
1758 int right = d->model->columnCount(d->root) - 1;
1759
1760 while (right >= 0 && isColumnHidden(d->logicalColumn(right)))
1761 --right;
1762
1763 if (bottom == -1 || right == -1)
1764 return QModelIndex(); // model is empty
1765
1766 QModelIndex current = currentIndex();
1767
1768 if (!current.isValid()) {
1769 int row = 0;
1770 int column = 0;
1771 while (column < right && isColumnHidden(d->logicalColumn(column)))
1772 ++column;
1773 while (isRowHidden(d->logicalRow(row)) && row < bottom)
1774 ++row;
1775 d->visualCursor = QPoint(column, row);
1776 return d->model->index(d->logicalRow(row), d->logicalColumn(column), d->root);
1777 }
1778
1779 // Update visual cursor if current index has changed.
1780 QPoint visualCurrent(d->visualColumn(current.column()), d->visualRow(current.row()));
1781 if (visualCurrent != d->visualCursor) {
1782 if (d->hasSpans()) {
1783 QSpanCollection::Span span = d->span(current.row(), current.column());
1784 if (span.top() > d->visualCursor.y() || d->visualCursor.y() > span.bottom()
1785 || span.left() > d->visualCursor.x() || d->visualCursor.x() > span.right())
1786 d->visualCursor = visualCurrent;
1787 } else {
1788 d->visualCursor = visualCurrent;
1789 }
1790 }
1791
1792 int visualRow = d->visualCursor.y();
1793 if (visualRow > bottom)
1794 visualRow = bottom;
1795 Q_ASSERT(visualRow != -1);
1796 int visualColumn = d->visualCursor.x();
1797 if (visualColumn > right)
1798 visualColumn = right;
1799 Q_ASSERT(visualColumn != -1);
1800
1801 if (isRightToLeft()) {
1802 if (cursorAction == MoveLeft)
1803 cursorAction = MoveRight;
1804 else if (cursorAction == MoveRight)
1805 cursorAction = MoveLeft;
1806 }
1807
1808 switch (cursorAction) {
1809 case MoveUp: {
1810 int originalRow = visualRow;
1811#ifdef QT_KEYPAD_NAVIGATION
1812 if (QApplicationPrivate::keypadNavigationEnabled() && visualRow == 0)
1813 visualRow = d->visualRow(model()->rowCount() - 1) + 1;
1814 // FIXME? visualRow = bottom + 1;
1815#endif
1816 int r = d->logicalRow(visualRow);
1817 int c = d->logicalColumn(visualColumn);
1818 if (r != -1 && d->hasSpans()) {
1819 QSpanCollection::Span span = d->span(r, c);
1820 if (span.width() > 1 || span.height() > 1)
1821 visualRow = d->visualRow(span.top());
1822 }
1823 while (visualRow >= 0) {
1824 --visualRow;
1825 r = d->logicalRow(visualRow);
1826 c = d->logicalColumn(visualColumn);
1827 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1828 break;
1829 }
1830 if (visualRow < 0)
1831 visualRow = originalRow;
1832 break;
1833 }
1834 case MoveDown: {
1835 int originalRow = visualRow;
1836 if (d->hasSpans()) {
1837 QSpanCollection::Span span = d->span(current.row(), current.column());
1838 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1839 }
1840#ifdef QT_KEYPAD_NAVIGATION
1841 if (QApplicationPrivate::keypadNavigationEnabled() && visualRow >= bottom)
1842 visualRow = -1;
1843#endif
1844 int r = d->logicalRow(visualRow);
1845 int c = d->logicalColumn(visualColumn);
1846 if (r != -1 && d->hasSpans()) {
1847 QSpanCollection::Span span = d->span(r, c);
1848 if (span.width() > 1 || span.height() > 1)
1849 visualRow = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
1850 }
1851 while (visualRow <= bottom) {
1852 ++visualRow;
1853 r = d->logicalRow(visualRow);
1854 c = d->logicalColumn(visualColumn);
1855 if (r == -1 || (!isRowHidden(r) && d->isCellEnabled(r, c)))
1856 break;
1857 }
1858 if (visualRow > bottom)
1859 visualRow = originalRow;
1860 break;
1861 }
1862 case MovePrevious:
1863 case MoveLeft: {
1864 int originalRow = visualRow;
1865 int originalColumn = visualColumn;
1866 bool firstTime = true;
1867 bool looped = false;
1868 bool wrapped = false;
1869 do {
1870 int r = d->logicalRow(visualRow);
1871 int c = d->logicalColumn(visualColumn);
1872 if (firstTime && c != -1 && d->hasSpans()) {
1873 firstTime = false;
1874 QSpanCollection::Span span = d->span(r, c);
1875 if (span.width() > 1 || span.height() > 1)
1876 visualColumn = d->visualColumn(span.left());
1877 }
1878 while (visualColumn >= 0) {
1879 --visualColumn;
1880 r = d->logicalRow(visualRow);
1881 c = d->logicalColumn(visualColumn);
1882 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1883 break;
1884 if (wrapped && (originalRow < visualRow || (originalRow == visualRow && originalColumn <= visualColumn))) {
1885 looped = true;
1886 break;
1887 }
1888 }
1889 if (cursorAction == MoveLeft || visualColumn >= 0)
1890 break;
1891 visualColumn = right + 1;
1892 if (visualRow == 0) {
1893 wrapped = true;
1894 visualRow = bottom;
1895 } else {
1896 --visualRow;
1897 }
1898 } while (!looped);
1899 if (visualColumn < 0)
1900 visualColumn = originalColumn;
1901 break;
1902 }
1903 case MoveNext:
1904 case MoveRight: {
1905 int originalRow = visualRow;
1906 int originalColumn = visualColumn;
1907 bool firstTime = true;
1908 bool looped = false;
1909 bool wrapped = false;
1910 do {
1911 int r = d->logicalRow(visualRow);
1912 int c = d->logicalColumn(visualColumn);
1913 if (firstTime && c != -1 && d->hasSpans()) {
1914 firstTime = false;
1915 QSpanCollection::Span span = d->span(r, c);
1916 if (span.width() > 1 || span.height() > 1)
1917 visualColumn = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
1918 }
1919 while (visualColumn <= right) {
1920 ++visualColumn;
1921 r = d->logicalRow(visualRow);
1922 c = d->logicalColumn(visualColumn);
1923 if (r == -1 || c == -1 || (!isRowHidden(r) && !isColumnHidden(c) && d->isCellEnabled(r, c)))
1924 break;
1925 if (wrapped && (originalRow > visualRow || (originalRow == visualRow && originalColumn >= visualColumn))) {
1926 looped = true;
1927 break;
1928 }
1929 }
1930 if (cursorAction == MoveRight || visualColumn <= right)
1931 break;
1932 visualColumn = -1;
1933 if (visualRow == bottom) {
1934 wrapped = true;
1935 visualRow = 0;
1936 } else {
1937 ++visualRow;
1938 }
1939 } while (!looped);
1940 if (visualColumn > right)
1941 visualColumn = originalColumn;
1942 break;
1943 }
1944 case MoveHome:
1945 visualColumn = d->nextActiveVisualColumn(visualRow, 0, right,
1948 visualRow = d->nextActiveVisualRow(0, visualColumn, bottom,
1950 break;
1951 case MoveEnd:
1952 visualColumn = d->nextActiveVisualColumn(visualRow, right, -1,
1955 visualRow = d->nextActiveVisualRow(bottom, visualColumn, -1,
1957 break;
1958 case MovePageUp: {
1959 int newLogicalRow = rowAt(visualRect(current).bottom() - d->viewport->height());
1960 int visualRow = (newLogicalRow == -1 ? 0 : d->visualRow(newLogicalRow));
1961 visualRow = d->nextActiveVisualRow(visualRow, current.column(), bottom,
1963 newLogicalRow = d->logicalRow(visualRow);
1964 return d->model->index(newLogicalRow, current.column(), d->root);
1965 }
1966 case MovePageDown: {
1967 int newLogicalRow = rowAt(visualRect(current).top() + d->viewport->height());
1968 int visualRow = (newLogicalRow == -1 ? bottom : d->visualRow(newLogicalRow));
1969 visualRow = d->nextActiveVisualRow(visualRow, current.column(), -1,
1971 newLogicalRow = d->logicalRow(visualRow);
1972 return d->model->index(newLogicalRow, current.column(), d->root);
1973 }}
1974
1975 d->visualCursor = QPoint(visualColumn, visualRow);
1976 int logicalRow = d->logicalRow(visualRow);
1977 int logicalColumn = d->logicalColumn(visualColumn);
1978 if (!d->model->hasIndex(logicalRow, logicalColumn, d->root))
1979 return QModelIndex();
1980
1981 QModelIndex result = d->model->index(logicalRow, logicalColumn, d->root);
1982 if (!d->isRowHidden(logicalRow) && !d->isColumnHidden(logicalColumn) && d->isIndexEnabled(result)) {
1983 if (d->hasSpans()) {
1984 QSpanCollection::Span span = d->span(result.row(), result.column());
1985 if (span.width() > 1 || span.height() > 1) {
1986 result = d->model->sibling(span.top(), span.left(), result);
1987 }
1988 }
1989 return result;
1990 }
1991
1992 return QModelIndex();
1993}
1994
2002void QTableView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command)
2003{
2004 Q_D(QTableView);
2005 QModelIndex tl = indexAt(QPoint(isRightToLeft() ? qMax(rect.left(), rect.right())
2006 : qMin(rect.left(), rect.right()), qMin(rect.top(), rect.bottom())));
2007 QModelIndex br = indexAt(QPoint(isRightToLeft() ? qMin(rect.left(), rect.right()) :
2008 qMax(rect.left(), rect.right()), qMax(rect.top(), rect.bottom())));
2009 if (!d->selectionModel || !tl.isValid() || !br.isValid() || !d->isIndexEnabled(tl) || !d->isIndexEnabled(br))
2010 return;
2011
2012 bool verticalMoved = verticalHeader()->sectionsMoved();
2013 bool horizontalMoved = horizontalHeader()->sectionsMoved();
2014
2016
2017 if (d->hasSpans()) {
2018 bool expanded;
2019 // when the current selection does not intersect with any spans of merged cells,
2020 // the range of selected cells must be the same as if there were no merged cells
2021 bool intersectsSpan = false;
2022 int top = qMin(d->visualRow(tl.row()), d->visualRow(br.row()));
2023 int left = qMin(d->visualColumn(tl.column()), d->visualColumn(br.column()));
2024 int bottom = qMax(d->visualRow(tl.row()), d->visualRow(br.row()));
2025 int right = qMax(d->visualColumn(tl.column()), d->visualColumn(br.column()));
2026 do {
2027 expanded = false;
2028 for (QSpanCollection::Span *it : d->spans.spans) {
2029 const QSpanCollection::Span &span = *it;
2030 int t = d->visualRow(span.top());
2031 int l = d->visualColumn(span.left());
2032 int b = d->visualRow(d->rowSpanEndLogical(span.top(), span.height()));
2033 int r = d->visualColumn(d->columnSpanEndLogical(span.left(), span.width()));
2034 if ((t > bottom) || (l > right) || (top > b) || (left > r))
2035 continue; // no intersect
2036 intersectsSpan = true;
2037 if (t < top) {
2038 top = t;
2039 expanded = true;
2040 }
2041 if (l < left) {
2042 left = l;
2043 expanded = true;
2044 }
2045 if (b > bottom) {
2046 bottom = b;
2047 expanded = true;
2048 }
2049 if (r > right) {
2050 right = r;
2051 expanded = true;
2052 }
2053 if (expanded)
2054 break;
2055 }
2056 } while (expanded);
2057 if (intersectsSpan) {
2058 selection.reserve((right - left + 1) * (bottom - top + 1));
2059 for (int horizontal = left; horizontal <= right; ++horizontal) {
2060 int column = d->logicalColumn(horizontal);
2061 for (int vertical = top; vertical <= bottom; ++vertical) {
2062 int row = d->logicalRow(vertical);
2063 QModelIndex index = d->model->index(row, column, d->root);
2065 }
2066 }
2067 } else {
2068 QItemSelectionRange range(tl, br);
2069 if (!range.isEmpty())
2071 }
2072 } else if (verticalMoved && horizontalMoved) {
2073 int top = d->visualRow(tl.row());
2074 int left = d->visualColumn(tl.column());
2075 int bottom = d->visualRow(br.row());
2076 int right = d->visualColumn(br.column());
2077 selection.reserve((right - left + 1) * (bottom - top + 1));
2078 for (int horizontal = left; horizontal <= right; ++horizontal) {
2079 int column = d->logicalColumn(horizontal);
2080 for (int vertical = top; vertical <= bottom; ++vertical) {
2081 int row = d->logicalRow(vertical);
2082 QModelIndex index = d->model->index(row, column, d->root);
2084 }
2085 }
2086 } else if (horizontalMoved) {
2087 int left = d->visualColumn(tl.column());
2088 int right = d->visualColumn(br.column());
2089 selection.reserve(right - left + 1);
2090 for (int visual = left; visual <= right; ++visual) {
2091 int column = d->logicalColumn(visual);
2092 QModelIndex topLeft = d->model->index(tl.row(), column, d->root);
2093 QModelIndex bottomRight = d->model->index(br.row(), column, d->root);
2094 selection.append(QItemSelectionRange(topLeft, bottomRight));
2095 }
2096 } else if (verticalMoved) {
2097 int top = d->visualRow(tl.row());
2098 int bottom = d->visualRow(br.row());
2099 selection.reserve(bottom - top + 1);
2100 for (int visual = top; visual <= bottom; ++visual) {
2101 int row = d->logicalRow(visual);
2102 QModelIndex topLeft = d->model->index(row, tl.column(), d->root);
2103 QModelIndex bottomRight = d->model->index(row, br.column(), d->root);
2104 selection.append(QItemSelectionRange(topLeft, bottomRight));
2105 }
2106 } else { // nothing moved
2107 QItemSelectionRange range(tl, br);
2108 if (!range.isEmpty())
2110 }
2111
2112 d->selectionModel->select(selection, command);
2113}
2114
2125{
2126 Q_D(const QTableView);
2127
2128 if (selection.isEmpty())
2129 return QRegion();
2130
2131 QRegion selectionRegion;
2132 const QRect &viewportRect = d->viewport->rect();
2133 bool verticalMoved = verticalHeader()->sectionsMoved();
2134 bool horizontalMoved = horizontalHeader()->sectionsMoved();
2135
2136 if ((verticalMoved && horizontalMoved) || (d->hasSpans() && (verticalMoved || horizontalMoved))) {
2137 for (const auto &range : selection) {
2138 if (range.parent() != d->root || !range.isValid())
2139 continue;
2140 for (int r = range.top(); r <= range.bottom(); ++r)
2141 for (int c = range.left(); c <= range.right(); ++c) {
2142 const QRect &rangeRect = visualRect(d->model->index(r, c, d->root));
2143 if (viewportRect.intersects(rangeRect))
2144 selectionRegion += rangeRect;
2145 }
2146 }
2147 } else if (horizontalMoved) {
2148 for (const auto &range : selection) {
2149 if (range.parent() != d->root || !range.isValid())
2150 continue;
2151 int top = rowViewportPosition(range.top());
2152 int bottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
2153 if (top > bottom)
2154 qSwap<int>(top, bottom);
2155 int height = bottom - top;
2156 for (int c = range.left(); c <= range.right(); ++c) {
2157 const QRect rangeRect(columnViewportPosition(c), top, columnWidth(c), height);
2158 if (viewportRect.intersects(rangeRect))
2159 selectionRegion += rangeRect;
2160 }
2161 }
2162 } else if (verticalMoved) {
2163 for (const auto &range : selection) {
2164 if (range.parent() != d->root || !range.isValid())
2165 continue;
2166 int left = columnViewportPosition(range.left());
2167 int right = columnViewportPosition(range.right()) + columnWidth(range.right());
2168 if (left > right)
2169 qSwap<int>(left, right);
2170 int width = right - left;
2171 for (int r = range.top(); r <= range.bottom(); ++r) {
2172 const QRect rangeRect(left, rowViewportPosition(r), width, rowHeight(r));
2173 if (viewportRect.intersects(rangeRect))
2174 selectionRegion += rangeRect;
2175 }
2176 }
2177 } else { // nothing moved
2178 const int gridAdjust = showGrid() ? 1 : 0;
2179 for (auto range : selection) {
2180 if (range.parent() != d->root || !range.isValid())
2181 continue;
2182 d->trimHiddenSelections(&range);
2183
2184 const int rtop = rowViewportPosition(range.top());
2185 const int rbottom = rowViewportPosition(range.bottom()) + rowHeight(range.bottom());
2186 int rleft;
2187 int rright;
2188 if (isLeftToRight()) {
2189 rleft = columnViewportPosition(range.left());
2190 rright = columnViewportPosition(range.right()) + columnWidth(range.right());
2191 } else {
2192 rleft = columnViewportPosition(range.right());
2193 rright = columnViewportPosition(range.left()) + columnWidth(range.left());
2194 }
2195 const QRect rangeRect(QPoint(rleft, rtop), QPoint(rright - 1 - gridAdjust, rbottom - 1 - gridAdjust));
2196 if (viewportRect.intersects(rangeRect))
2197 selectionRegion += rangeRect;
2198 if (d->hasSpans()) {
2199 const auto spansInRect = d->spans.spansInRect(range.left(), range.top(), range.width(), range.height());
2200 for (QSpanCollection::Span *s : spansInRect) {
2201 if (range.contains(s->top(), s->left(), range.parent())) {
2202 const QRect &visualSpanRect = d->visualSpanRect(*s);
2203 if (viewportRect.intersects(visualSpanRect))
2204 selectionRegion += visualSpanRect;
2205 }
2206 }
2207 }
2208 }
2209 }
2210
2211 return selectionRegion;
2212}
2213
2214
2219{
2220 Q_D(const QTableView);
2221 QModelIndexList viewSelected;
2222 QModelIndexList modelSelected;
2223 if (d->selectionModel)
2224 modelSelected = d->selectionModel->selectedIndexes();
2225 for (int i = 0; i < modelSelected.size(); ++i) {
2226 QModelIndex index = modelSelected.at(i);
2227 if (!isIndexHidden(index) && index.parent() == d->root)
2228 viewSelected.append(index);
2229 }
2230 return viewSelected;
2231}
2232
2233
2239void QTableView::rowCountChanged(int oldCount, int newCount )
2240{
2241 Q_D(QTableView);
2242 //when removing rows, we need to disable updates for the header until the geometries have been
2243 //updated and the offset has been adjusted, or we risk calling paintSection for all the sections
2244 if (newCount < oldCount)
2245 d->verticalHeader->setUpdatesEnabled(false);
2246 d->doDelayedItemsLayout();
2247}
2248
2255{
2256 Q_D(QTableView);
2259 d->horizontalHeader->setOffsetToSectionPosition(horizontalScrollBar()->value());
2260 else
2261 d->horizontalHeader->setOffset(horizontalScrollBar()->value());
2262 d->viewport->update();
2263}
2264
2269{
2270 Q_D(QTableView);
2271 if (d->geometryRecursionBlock)
2272 return;
2273 d->geometryRecursionBlock = true;
2274
2275 int width = 0;
2276 if (!d->verticalHeader->isHidden()) {
2277 width = qMax(d->verticalHeader->minimumWidth(), d->verticalHeader->sizeHint().width());
2278 width = qMin(width, d->verticalHeader->maximumWidth());
2279 }
2280 int height = 0;
2281 if (!d->horizontalHeader->isHidden()) {
2282 height = qMax(d->horizontalHeader->minimumHeight(), d->horizontalHeader->sizeHint().height());
2283 height = qMin(height, d->horizontalHeader->maximumHeight());
2284 }
2285 bool reverse = isRightToLeft();
2286 if (reverse)
2287 setViewportMargins(0, height, width, 0);
2288 else
2289 setViewportMargins(width, height, 0, 0);
2290
2291 // update headers
2292
2293 QRect vg = d->viewport->geometry();
2294
2295 int verticalLeft = reverse ? vg.right() + 1 : (vg.left() - width);
2296 d->verticalHeader->setGeometry(verticalLeft, vg.top(), width, vg.height());
2297 if (d->verticalHeader->isHidden())
2298 QMetaObject::invokeMethod(d->verticalHeader, "updateGeometries");
2299
2300 int horizontalTop = vg.top() - height;
2301 d->horizontalHeader->setGeometry(vg.left(), horizontalTop, vg.width(), height);
2302 if (d->horizontalHeader->isHidden())
2303 QMetaObject::invokeMethod(d->horizontalHeader, "updateGeometries");
2304
2305#if QT_CONFIG(abstractbutton)
2306 // update cornerWidget
2307 if (d->horizontalHeader->isHidden() || d->verticalHeader->isHidden()) {
2308 d->cornerWidget->setHidden(true);
2309 } else {
2310 d->cornerWidget->setHidden(false);
2311 d->cornerWidget->setGeometry(verticalLeft, horizontalTop, width, height);
2312 }
2313#endif
2314
2315 // update scroll bars
2316
2317 // ### move this block into the if
2318 QSize vsize = d->viewport->size();
2319 QSize max = maximumViewportSize();
2320 const int horizontalLength = d->horizontalHeader->length();
2321 const int verticalLength = d->verticalHeader->length();
2322 if (max.width() >= horizontalLength && max.height() >= verticalLength)
2323 vsize = max;
2324
2325 // horizontal scroll bar
2326 const int columnCount = d->horizontalHeader->count();
2327 const int viewportWidth = vsize.width();
2328 int columnsInViewport = 0;
2329 for (int width = 0, column = columnCount - 1; column >= 0; --column) {
2330 int logical = d->horizontalHeader->logicalIndex(column);
2331 if (!d->horizontalHeader->isSectionHidden(logical)) {
2332 width += d->horizontalHeader->sectionSize(logical);
2333 if (width > viewportWidth)
2334 break;
2335 ++columnsInViewport;
2336 }
2337 }
2338 columnsInViewport = qMax(columnsInViewport, 1); //there must be always at least 1 column
2339
2341 const int visibleColumns = columnCount - d->horizontalHeader->hiddenSectionCount();
2342 horizontalScrollBar()->setRange(0, visibleColumns - columnsInViewport);
2343 horizontalScrollBar()->setPageStep(columnsInViewport);
2344 if (columnsInViewport >= visibleColumns)
2345 d->horizontalHeader->setOffset(0);
2346 horizontalScrollBar()->setSingleStep(1);
2347 } else { // ScrollPerPixel
2348 horizontalScrollBar()->setPageStep(vsize.width());
2349 horizontalScrollBar()->setRange(0, horizontalLength - vsize.width());
2350 horizontalScrollBar()->d_func()->itemviewChangeSingleStep(qMax(vsize.width() / (columnsInViewport + 1), 2));
2351 }
2352
2353 // vertical scroll bar
2354 const int rowCount = d->verticalHeader->count();
2355 const int viewportHeight = vsize.height();
2356 int rowsInViewport = 0;
2357 for (int height = 0, row = rowCount - 1; row >= 0; --row) {
2358 int logical = d->verticalHeader->logicalIndex(row);
2359 if (!d->verticalHeader->isSectionHidden(logical)) {
2360 height += d->verticalHeader->sectionSize(logical);
2361 if (height > viewportHeight)
2362 break;
2363 ++rowsInViewport;
2364 }
2365 }
2366 rowsInViewport = qMax(rowsInViewport, 1); //there must be always at least 1 row
2367
2369 const int visibleRows = rowCount - d->verticalHeader->hiddenSectionCount();
2370 verticalScrollBar()->setRange(0, visibleRows - rowsInViewport);
2371 verticalScrollBar()->setPageStep(rowsInViewport);
2372 if (rowsInViewport >= visibleRows)
2373 d->verticalHeader->setOffset(0);
2374 verticalScrollBar()->setSingleStep(1);
2375 } else { // ScrollPerPixel
2376 verticalScrollBar()->setPageStep(vsize.height());
2377 verticalScrollBar()->setRange(0, verticalLength - vsize.height());
2378 verticalScrollBar()->d_func()->itemviewChangeSingleStep(qMax(vsize.height() / (rowsInViewport + 1), 2));
2379 }
2380 d->verticalHeader->d_func()->setScrollOffset(verticalScrollBar(), verticalScrollMode());
2381
2382 d->geometryRecursionBlock = false;
2384}
2385
2401{
2402 Q_D(const QTableView);
2403
2404 if (!model())
2405 return -1;
2406
2407 ensurePolished();
2408 const int maximumProcessCols = d->verticalHeader->resizeContentsPrecision();
2409
2410
2411 int left = qMax(0, d->horizontalHeader->visualIndexAt(0));
2412 int right = d->horizontalHeader->visualIndexAt(d->viewport->width());
2413 if (right == -1) // the table don't have enough columns to fill the viewport
2414 right = d->model->columnCount(d->root) - 1;
2415
2416 QStyleOptionViewItem option;
2418
2419 int hint = 0;
2421 int columnsProcessed = 0;
2422 int column = left;
2423 for (; column <= right; ++column) {
2424 int logicalColumn = d->horizontalHeader->logicalIndex(column);
2425 if (d->horizontalHeader->isSectionHidden(logicalColumn))
2426 continue;
2427 index = d->model->index(row, logicalColumn, d->root);
2428 hint = d->heightHintForIndex(index, hint, option);
2429
2430 ++columnsProcessed;
2431 if (columnsProcessed == maximumProcessCols)
2432 break;
2433 }
2434
2435 const int actualRight = d->model->columnCount(d->root) - 1;
2436 int idxLeft = left;
2437 int idxRight = column - 1;
2438
2439 if (maximumProcessCols == 0 || actualRight < idxLeft)
2440 columnsProcessed = maximumProcessCols; // skip the while loop
2441
2442 while (columnsProcessed != maximumProcessCols && (idxLeft > 0 || idxRight < actualRight)) {
2443 int logicalIdx = -1;
2444
2445 if ((columnsProcessed % 2 && idxLeft > 0) || idxRight == actualRight) {
2446 while (idxLeft > 0) {
2447 --idxLeft;
2448 int logcol = d->horizontalHeader->logicalIndex(idxLeft);
2449 if (d->horizontalHeader->isSectionHidden(logcol))
2450 continue;
2451 logicalIdx = logcol;
2452 break;
2453 }
2454 } else {
2455 while (idxRight < actualRight) {
2456 ++idxRight;
2457 int logcol = d->horizontalHeader->logicalIndex(idxRight);
2458 if (d->horizontalHeader->isSectionHidden(logcol))
2459 continue;
2460 logicalIdx = logcol;
2461 break;
2462 }
2463 }
2464 if (logicalIdx >= 0) {
2465 index = d->model->index(row, logicalIdx, d->root);
2466 hint = d->heightHintForIndex(index, hint, option);
2467 }
2468 ++columnsProcessed;
2469 }
2470
2471 return d->showGrid ? hint + 1 : hint;
2472}
2473
2490{
2491 Q_D(const QTableView);
2492
2493 if (!model())
2494 return -1;
2495
2496 ensurePolished();
2497 const int maximumProcessRows = d->horizontalHeader->resizeContentsPrecision();
2498
2499 int top = qMax(0, d->verticalHeader->visualIndexAt(0));
2500 int bottom = d->verticalHeader->visualIndexAt(d->viewport->height());
2501 if (!isVisible() || bottom == -1) // the table don't have enough rows to fill the viewport
2502 bottom = d->model->rowCount(d->root) - 1;
2503
2504 QStyleOptionViewItem option;
2506
2507 int hint = 0;
2508 int rowsProcessed = 0;
2510 int row = top;
2511 for (; row <= bottom; ++row) {
2512 int logicalRow = d->verticalHeader->logicalIndex(row);
2513 if (d->verticalHeader->isSectionHidden(logicalRow))
2514 continue;
2515 index = d->model->index(logicalRow, column, d->root);
2516
2517 hint = d->widthHintForIndex(index, hint, option);
2518 ++rowsProcessed;
2519 if (rowsProcessed == maximumProcessRows)
2520 break;
2521 }
2522
2523 const int actualBottom = d->model->rowCount(d->root) - 1;
2524 int idxTop = top;
2525 int idxBottom = row - 1;
2526
2527 if (maximumProcessRows == 0 || actualBottom < idxTop)
2528 rowsProcessed = maximumProcessRows; // skip the while loop
2529
2530 while (rowsProcessed != maximumProcessRows && (idxTop > 0 || idxBottom < actualBottom)) {
2531 int logicalIdx = -1;
2532
2533 if ((rowsProcessed % 2 && idxTop > 0) || idxBottom == actualBottom) {
2534 while (idxTop > 0) {
2535 --idxTop;
2536 int logrow = d->verticalHeader->logicalIndex(idxTop);
2537 if (d->verticalHeader->isSectionHidden(logrow))
2538 continue;
2539 logicalIdx = logrow;
2540 break;
2541 }
2542 } else {
2543 while (idxBottom < actualBottom) {
2544 ++idxBottom;
2545 int logrow = d->verticalHeader->logicalIndex(idxBottom);
2546 if (d->verticalHeader->isSectionHidden(logrow))
2547 continue;
2548 logicalIdx = logrow;
2549 break;
2550 }
2551 }
2552 if (logicalIdx >= 0) {
2553 index = d->model->index(logicalIdx, column, d->root);
2554 hint = d->widthHintForIndex(index, hint, option);
2555 }
2556 ++rowsProcessed;
2557 }
2558
2559 return d->showGrid ? hint + 1 : hint;
2560}
2561
2567{
2568 Q_D(const QTableView);
2569 return d->verticalHeader->sectionViewportPosition(row);
2570}
2571
2581int QTableView::rowAt(int y) const
2582{
2583 Q_D(const QTableView);
2584 return d->verticalHeader->logicalIndexAt(y);
2585}
2586
2593{
2594 Q_D(const QTableView);
2595 d->verticalHeader->resizeSection(row, height);
2596}
2597
2604{
2605 Q_D(const QTableView);
2606 return d->verticalHeader->sectionSize(row);
2607}
2608
2614{
2615 Q_D(const QTableView);
2616 return d->horizontalHeader->sectionViewportPosition(column);
2617}
2618
2629{
2630 Q_D(const QTableView);
2631 return d->horizontalHeader->logicalIndexAt(x);
2632}
2633
2640{
2641 Q_D(const QTableView);
2642 d->horizontalHeader->resizeSection(column, width);
2643}
2644
2651{
2652 Q_D(const QTableView);
2653 return d->horizontalHeader->sectionSize(column);
2654}
2655
2662{
2663 Q_D(const QTableView);
2664 return d->verticalHeader->isSectionHidden(row);
2665}
2666
2673{
2674 Q_D(QTableView);
2675 if (row < 0 || row >= d->verticalHeader->count())
2676 return;
2677 d->verticalHeader->setSectionHidden(row, hide);
2678}
2679
2686{
2687 Q_D(const QTableView);
2688 return d->horizontalHeader->isSectionHidden(column);
2689}
2690
2698{
2699 Q_D(QTableView);
2700 if (column < 0 || column >= d->horizontalHeader->count())
2701 return;
2702 d->horizontalHeader->setSectionHidden(column, hide);
2703}
2704
2727{
2728 Q_D(QTableView);
2730 for (const QMetaObject::Connection &connection : d->dynHorHeaderConnections)
2732 d->dynHorHeaderConnections.clear();
2733 if (enable) {
2734 //sortByColumn has to be called before we connect or set the sortingEnabled flag
2735 // because otherwise it will not call sort on the model.
2736 sortByColumn(d->horizontalHeader->sortIndicatorSection(),
2737 d->horizontalHeader->sortIndicatorOrder());
2738 d->dynHorHeaderConnections = {
2741 };
2742 } else {
2743 d->dynHorHeaderConnections = {
2744 connect(d->horizontalHeader, &QHeaderView::sectionPressed,
2746 connect(d->horizontalHeader, &QHeaderView::sectionEntered,
2747 this, [d](int column) {d->selectColumn(column, false); })
2748 };
2749 }
2750 d->sortingEnabled = enable;
2751}
2752
2754{
2755 Q_D(const QTableView);
2756 return d->sortingEnabled;
2757}
2758
2767{
2768 Q_D(const QTableView);
2769 return d->showGrid;
2770}
2771
2773{
2774 Q_D(QTableView);
2775 if (d->showGrid != show) {
2776 d->showGrid = show;
2777 d->viewport->update();
2778 }
2779}
2780
2788{
2789 Q_D(const QTableView);
2790 return d->gridStyle;
2791}
2792
2794{
2795 Q_D(QTableView);
2796 if (d->gridStyle != style) {
2797 d->gridStyle = style;
2798 d->viewport->update();
2799 }
2800}
2801
2817{
2818 Q_D(QTableView);
2819 if (d->wrapItemText == on)
2820 return;
2821 d->wrapItemText = on;
2822 QMetaObject::invokeMethod(d->verticalHeader, "resizeSections");
2823 QMetaObject::invokeMethod(d->horizontalHeader, "resizeSections");
2824}
2825
2827{
2828 Q_D(const QTableView);
2829 return d->wrapItemText;
2830}
2831
2832#if QT_CONFIG(abstractbutton)
2844void QTableView::setCornerButtonEnabled(bool enable)
2845{
2846 Q_D(QTableView);
2847 d->cornerWidget->setEnabled(enable);
2848}
2849
2850bool QTableView::isCornerButtonEnabled() const
2851{
2852 Q_D(const QTableView);
2853 return d->cornerWidget->isEnabled();
2854}
2855#endif
2856
2865{
2866 Q_D(const QTableView);
2867 if (!d->isIndexValid(index) || index.parent() != d->root
2868 || (!d->hasSpans() && isIndexHidden(index)))
2869 return QRect();
2870
2871 d->executePostedLayout();
2872
2873 if (d->hasSpans()) {
2874 QSpanCollection::Span span = d->span(index.row(), index.column());
2875 return d->visualSpanRect(span);
2876 }
2877
2878 int rowp = rowViewportPosition(index.row());
2879 int rowh = rowHeight(index.row());
2880 int colp = columnViewportPosition(index.column());
2881 int colw = columnWidth(index.column());
2882
2883 const int i = showGrid() ? 1 : 0;
2884 return QRect(colp, rowp, colw - i, rowh - i);
2885}
2886
2894{
2895 Q_D(QTableView);
2896
2897 // check if we really need to do anything
2898 if (!d->isIndexValid(index)
2899 || (d->model->parent(index) != d->root)
2900 || isRowHidden(index.row()) || isColumnHidden(index.column()))
2901 return;
2902
2904 if (d->hasSpans())
2905 span = d->span(index.row(), index.column());
2906
2907 // Adjust horizontal position
2908
2909 int viewportWidth = d->viewport->width();
2910 int horizontalOffset = d->horizontalHeader->offset();
2911 int horizontalPosition = d->horizontalHeader->sectionPosition(index.column());
2912 int horizontalIndex = d->horizontalHeader->visualIndex(index.column());
2913 int cellWidth = d->hasSpans()
2914 ? d->columnSpanWidth(index.column(), span.width())
2915 : d->horizontalHeader->sectionSize(index.column());
2916
2918
2919 bool positionAtLeft = (horizontalPosition - horizontalOffset < 0);
2920 bool positionAtRight = (horizontalPosition - horizontalOffset + cellWidth > viewportWidth);
2921
2922 if (hint == PositionAtCenter || positionAtRight) {
2923 int w = (hint == PositionAtCenter ? viewportWidth / 2 : viewportWidth);
2924 int x = cellWidth;
2925 while (horizontalIndex > 0) {
2926 x += columnWidth(d->horizontalHeader->logicalIndex(horizontalIndex-1));
2927 if (x > w)
2928 break;
2929 --horizontalIndex;
2930 }
2931 }
2932
2933 if (positionAtRight || hint == PositionAtCenter || positionAtLeft) {
2934 int hiddenSections = 0;
2935 if (d->horizontalHeader->sectionsHidden()) {
2936 for (int s = horizontalIndex - 1; s >= 0; --s) {
2937 int column = d->horizontalHeader->logicalIndex(s);
2938 if (d->horizontalHeader->isSectionHidden(column))
2939 ++hiddenSections;
2940 }
2941 }
2942 horizontalScrollBar()->setValue(horizontalIndex - hiddenSections);
2943 }
2944
2945 } else { // ScrollPerPixel
2946 if (hint == PositionAtCenter) {
2947 horizontalScrollBar()->setValue(horizontalPosition - ((viewportWidth - cellWidth) / 2));
2948 } else {
2949 if (horizontalPosition - horizontalOffset < 0 || cellWidth > viewportWidth)
2950 horizontalScrollBar()->setValue(horizontalPosition);
2951 else if (horizontalPosition - horizontalOffset + cellWidth > viewportWidth)
2952 horizontalScrollBar()->setValue(horizontalPosition - viewportWidth + cellWidth);
2953 }
2954 }
2955
2956 // Adjust vertical position
2957
2958 int viewportHeight = d->viewport->height();
2959 int verticalOffset = d->verticalHeader->offset();
2960 int verticalPosition = d->verticalHeader->sectionPosition(index.row());
2961 int verticalIndex = d->verticalHeader->visualIndex(index.row());
2962 int cellHeight = d->hasSpans()
2963 ? d->rowSpanHeight(index.row(), span.height())
2964 : d->verticalHeader->sectionSize(index.row());
2965
2966 if (verticalPosition - verticalOffset < 0 || cellHeight > viewportHeight) {
2967 if (hint == EnsureVisible)
2969 } else if (verticalPosition - verticalOffset + cellHeight > viewportHeight) {
2970 if (hint == EnsureVisible)
2972 }
2973
2975
2977 int h = (hint == PositionAtCenter ? viewportHeight / 2 : viewportHeight);
2978 int y = cellHeight;
2979 while (verticalIndex > 0) {
2980 int row = d->verticalHeader->logicalIndex(verticalIndex - 1);
2981 y += d->verticalHeader->sectionSize(row);
2982 if (y > h)
2983 break;
2984 --verticalIndex;
2985 }
2986 }
2987
2989 int hiddenSections = 0;
2990 if (d->verticalHeader->sectionsHidden()) {
2991 for (int s = verticalIndex - 1; s >= 0; --s) {
2992 int row = d->verticalHeader->logicalIndex(s);
2993 if (d->verticalHeader->isSectionHidden(row))
2994 ++hiddenSections;
2995 }
2996 }
2997 verticalScrollBar()->setValue(verticalIndex - hiddenSections);
2998 }
2999
3000 } else { // ScrollPerPixel
3001 if (hint == PositionAtTop) {
3002 verticalScrollBar()->setValue(verticalPosition);
3003 } else if (hint == PositionAtBottom) {
3004 verticalScrollBar()->setValue(verticalPosition - viewportHeight + cellHeight);
3005 } else if (hint == PositionAtCenter) {
3006 verticalScrollBar()->setValue(verticalPosition - ((viewportHeight - cellHeight) / 2));
3007 }
3008 }
3009
3010 update(index);
3011}
3012
3020void QTableView::rowResized(int row, int, int)
3021{
3022 Q_D(QTableView);
3023 d->rowsToUpdate.append(row);
3024 if (d->rowResizeTimerID == 0)
3025 d->rowResizeTimerID = startTimer(0);
3026}
3027
3036{
3037 Q_D(QTableView);
3038 d->columnsToUpdate.append(column);
3039 if (d->columnResizeTimerID == 0)
3040 d->columnResizeTimerID = startTimer(0);
3041}
3042
3047{
3048 Q_D(QTableView);
3049
3050 if (event->timerId() == d->columnResizeTimerID) {
3051 const int oldScrollMax = horizontalScrollBar()->maximum();
3054 killTimer(d->columnResizeTimerID);
3055 d->columnResizeTimerID = 0;
3056 } else {
3058 }
3059
3060 QRect rect;
3061 int viewportHeight = d->viewport->height();
3062 int viewportWidth = d->viewport->width();
3063 if (d->hasSpans() || horizontalScrollBar()->value() == oldScrollMax) {
3064 rect = QRect(0, 0, viewportWidth, viewportHeight);
3065 } else {
3066 for (int i = d->columnsToUpdate.size()-1; i >= 0; --i) {
3067 int column = d->columnsToUpdate.at(i);
3069 if (isRightToLeft())
3070 rect |= QRect(0, 0, x + columnWidth(column), viewportHeight);
3071 else
3072 rect |= QRect(x, 0, viewportWidth - x, viewportHeight);
3073 }
3074 }
3075
3076 d->viewport->update(rect.normalized());
3077 d->columnsToUpdate.clear();
3078 }
3079
3080 if (event->timerId() == d->rowResizeTimerID) {
3081 const int oldScrollMax = verticalScrollBar()->maximum();
3084 killTimer(d->rowResizeTimerID);
3085 d->rowResizeTimerID = 0;
3086 } else {
3088 }
3089
3090 int viewportHeight = d->viewport->height();
3091 int viewportWidth = d->viewport->width();
3092 int top;
3093 if (d->hasSpans() || verticalScrollBar()->value() == oldScrollMax) {
3094 top = 0;
3095 } else {
3096 top = viewportHeight;
3097 for (int i = d->rowsToUpdate.size()-1; i >= 0; --i) {
3098 int y = rowViewportPosition(d->rowsToUpdate.at(i));
3099 top = qMin(top, y);
3100 }
3101 }
3102
3103 d->viewport->update(QRect(0, top, viewportWidth, viewportHeight - top));
3104 d->rowsToUpdate.clear();
3105 }
3106
3108}
3109
3117void QTableView::rowMoved(int row, int oldIndex, int newIndex)
3118{
3119 Q_UNUSED(row);
3120 Q_D(QTableView);
3121
3123 int logicalOldIndex = d->verticalHeader->logicalIndex(oldIndex);
3124 int logicalNewIndex = d->verticalHeader->logicalIndex(newIndex);
3125 if (d->hasSpans()) {
3126 d->viewport->update();
3127 } else {
3128 int oldTop = rowViewportPosition(logicalOldIndex);
3129 int newTop = rowViewportPosition(logicalNewIndex);
3130 int oldBottom = oldTop + rowHeight(logicalOldIndex);
3131 int newBottom = newTop + rowHeight(logicalNewIndex);
3132 int top = qMin(oldTop, newTop);
3133 int bottom = qMax(oldBottom, newBottom);
3134 int height = bottom - top;
3135 d->viewport->update(0, top, d->viewport->width(), height);
3136 }
3137}
3138
3146void QTableView::columnMoved(int column, int oldIndex, int newIndex)
3147{
3149 Q_D(QTableView);
3150
3152 int logicalOldIndex = d->horizontalHeader->logicalIndex(oldIndex);
3153 int logicalNewIndex = d->horizontalHeader->logicalIndex(newIndex);
3154 if (d->hasSpans()) {
3155 d->viewport->update();
3156 } else {
3157 int oldLeft = columnViewportPosition(logicalOldIndex);
3158 int newLeft = columnViewportPosition(logicalNewIndex);
3159 int oldRight = oldLeft + columnWidth(logicalOldIndex);
3160 int newRight = newLeft + columnWidth(logicalNewIndex);
3161 int left = qMin(oldLeft, newLeft);
3162 int right = qMax(oldRight, newRight);
3163 int width = right - left;
3164 d->viewport->update(left, 0, width, d->viewport->height());
3165 }
3166}
3167
3175{
3176 Q_D(QTableView);
3177 d->selectRow(row, true);
3178}
3179
3187{
3188 Q_D(QTableView);
3189 d->selectColumn(column, true);
3190}
3191
3198{
3199 Q_D(QTableView);
3200 d->verticalHeader->hideSection(row);
3201}
3202
3209{
3210 Q_D(QTableView);
3211 d->horizontalHeader->hideSection(column);
3212}
3213
3220{
3221 Q_D(QTableView);
3222 d->verticalHeader->showSection(row);
3223}
3224
3231{
3232 Q_D(QTableView);
3233 d->horizontalHeader->showSection(column);
3234}
3235
3243{
3244 Q_D(QTableView);
3245 int content = sizeHintForRow(row);
3246 int header = d->verticalHeader->sectionSizeHint(row);
3247 d->verticalHeader->resizeSection(row, qMax(content, header));
3248}
3249
3257{
3258 Q_D(QTableView);
3259 d->verticalHeader->resizeSections(QHeaderView::ResizeToContents);
3260}
3261
3272{
3273 Q_D(QTableView);
3274 int content = sizeHintForColumn(column);
3275 int header = d->horizontalHeader->sectionSizeHint(column);
3276 d->horizontalHeader->resizeSection(column, qMax(content, header));
3277}
3278
3286{
3287 Q_D(QTableView);
3288 d->horizontalHeader->resizeSections(QHeaderView::ResizeToContents);
3289}
3290
3303{
3304 Q_D(QTableView);
3305 if (column < -1)
3306 return;
3307 d->horizontalHeader->setSortIndicator(column, order);
3308 // If sorting is not enabled or has the same order as before, force to sort now
3309 // else sorting will be trigger through sortIndicatorChanged()
3310 if (!d->sortingEnabled ||
3311 (d->horizontalHeader->sortIndicatorSection() == column && d->horizontalHeader->sortIndicatorOrder() == order))
3312 d->model->sort(column, order);
3313}
3314
3322
3330
3335{
3336 Q_D(const QTableView);
3337 Q_ASSERT(d->isIndexValid(index));
3338 if (isRowHidden(index.row()) || isColumnHidden(index.column()))
3339 return true;
3340 if (d->hasSpans()) {
3341 QSpanCollection::Span span = d->span(index.row(), index.column());
3342 return !((span.top() == index.row()) && (span.left() == index.column()));
3343 }
3344 return false;
3345}
3346
3356void QTableView::setSpan(int row, int column, int rowSpan, int columnSpan)
3357{
3358 Q_D(QTableView);
3359 if (row < 0 || column < 0 || rowSpan < 0 || columnSpan < 0)
3360 return;
3361 d->setSpan(row, column, rowSpan, columnSpan);
3362 d->viewport->update();
3363}
3364
3373int QTableView::rowSpan(int row, int column) const
3374{
3375 Q_D(const QTableView);
3376 return d->rowSpan(row, column);
3377}
3378
3388{
3389 Q_D(const QTableView);
3390 return d->columnSpan(row, column);
3391}
3392
3402{
3403 Q_D(QTableView);
3404 d->spans.clear();
3405 d->viewport->update();
3406}
3407
3408void QTableViewPrivate::selectRow(int row, bool anchor)
3409{
3410 Q_Q(QTableView);
3411
3412 if (q->selectionBehavior() == QTableView::SelectColumns
3413 || (q->selectionMode() == QTableView::SingleSelection
3414 && q->selectionBehavior() == QTableView::SelectItems))
3415 return;
3416
3417 if (row >= 0 && row < model->rowCount(root)) {
3418 int column = horizontalHeader->logicalIndexAt(q->isRightToLeft() ? viewport->width() : 0);
3420 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3422 if ((anchor && !(command & QItemSelectionModel::Current))
3423 || (q->selectionMode() == QTableView::SingleSelection))
3425
3426 if (q->selectionMode() != QTableView::SingleSelection
3427 && command.testFlag(QItemSelectionModel::Toggle)) {
3428 if (anchor)
3431 command &= ~QItemSelectionModel::Toggle;
3432 command |= ctrlDragSelectionFlag;
3433 if (!anchor)
3435 }
3436
3437 const auto rowSectionAnchor = currentSelectionStartIndex.row();
3438 QModelIndex upper = model->index(qMin(rowSectionAnchor, row), column, root);
3439 QModelIndex lower = model->index(qMax(rowSectionAnchor, row), column, root);
3440 if ((verticalHeader->sectionsMoved() && upper.row() != lower.row())) {
3441 q->setSelection(q->visualRect(upper) | q->visualRect(lower), command | QItemSelectionModel::Rows);
3442 } else {
3444 }
3445 }
3446}
3447
3449{
3450 Q_Q(QTableView);
3451
3452 if (q->selectionBehavior() == QTableView::SelectRows
3453 || (q->selectionMode() == QTableView::SingleSelection
3454 && q->selectionBehavior() == QTableView::SelectItems))
3455 return;
3456
3457 if (column >= 0 && column < model->columnCount(root)) {
3460 QItemSelectionModel::SelectionFlags command = q->selectionCommand(index);
3462 if ((anchor && !(command & QItemSelectionModel::Current))
3463 || (q->selectionMode() == QTableView::SingleSelection))
3465
3466 if (q->selectionMode() != QTableView::SingleSelection
3467 && command.testFlag(QItemSelectionModel::Toggle)) {
3468 if (anchor)
3471 command &= ~QItemSelectionModel::Toggle;
3472 command |= ctrlDragSelectionFlag;
3473 if (!anchor)
3475 }
3476
3477 const auto columnSectionAnchor = currentSelectionStartIndex.column();
3478 QModelIndex left = model->index(row, qMin(columnSectionAnchor, column), root);
3479 QModelIndex right = model->index(row, qMax(columnSectionAnchor, column), root);
3480 if ((horizontalHeader->sectionsMoved() && left.column() != right.column())) {
3481 q->setSelection(q->visualRect(left) | q->visualRect(right), command | QItemSelectionModel::Columns);
3482 } else {
3484 }
3485 }
3486}
3487
3491void QTableView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
3492{
3493#if QT_CONFIG(accessibility)
3494 if (QAccessible::isActive()) {
3495 if (current.isValid() && hasFocus()) {
3496 Q_D(QTableView);
3497 int entry = d->accessibleTable2Index(current);
3498 QAccessibleEvent event(this, QAccessible::Focus);
3499 event.setChild(entry);
3500 QAccessible::updateAccessibility(&event);
3501 }
3502 }
3503#endif
3504 QAbstractItemView::currentChanged(current, previous);
3505}
3506
3511 const QItemSelection &deselected)
3512{
3513 Q_D(QTableView);
3514 Q_UNUSED(d);
3515#if QT_CONFIG(accessibility)
3516 if (QAccessible::isActive()) {
3517 // ### does not work properly for selection ranges.
3518 QModelIndex sel = selected.indexes().value(0);
3519 if (sel.isValid()) {
3520 int entry = d->accessibleTable2Index(sel);
3521 QAccessibleEvent event(this, QAccessible::SelectionAdd);
3522 event.setChild(entry);
3523 QAccessible::updateAccessibility(&event);
3524 }
3525 QModelIndex desel = deselected.indexes().value(0);
3526 if (desel.isValid()) {
3527 int entry = d->accessibleTable2Index(desel);
3528 QAccessibleEvent event(this, QAccessible::SelectionRemove);
3529 event.setChild(entry);
3530 QAccessible::updateAccessibility(&event);
3531 }
3532 }
3533#endif
3534 QAbstractItemView::selectionChanged(selected, deselected);
3535}
3536
3537int QTableView::visualIndex(const QModelIndex &index) const
3538{
3539 return index.row();
3540}
3541
3543
3544#include "qtableview.moc"
3545
3546#include "moc_qtableview.cpp"
The QAbstractButton class is the abstract base class of button widgets, providing functionality commo...
static QAbstractItemModel * staticEmptyModel()
void columnsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been removed from the model.
virtual Q_INVOKABLE Qt::ItemFlags flags(const QModelIndex &index) const
Returns the item flags for the given index.
void rowsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been inserted into the model.
void columnsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after columns have been inserted into the model.
virtual Q_INVOKABLE QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const =0
Returns the index of the item in the model specified by the given row, column and parent index.
void rowsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been removed from the model.
virtual bool submit()
Lets the model know that it should submit cached information to permanent storage.
const QEditorInfo & editorForIndex(const QModelIndex &index) const
QAbstractItemView::EditTriggers editTriggers
QPersistentModelIndex currentSelectionStartIndex
QPointer< QItemSelectionModel > selectionModel
QItemSelectionModel::SelectionFlag ctrlDragSelectionFlag
QPersistentModelIndex root
QWidget * editor(const QModelIndex &index, const QStyleOptionViewItem &options)
QPersistentModelIndex hover
The QAbstractItemView class provides the basic functionality for item view classes.
QAbstractItemModel * model() const
Returns the model that this view is presenting.
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
Sets the current selection model to the given selectionModel.
void timerEvent(QTimerEvent *event) override
This function is called with the given event when a timer event is sent to the widget.
ScrollMode verticalScrollMode
how the view scrolls its contents in the vertical direction
virtual void verticalScrollbarAction(int action)
virtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
This slot is called when the selection is changed.
State state() const
Returns the item view's state.
QModelIndex currentIndex() const
Returns the model index of the current item.
virtual void setModel(QAbstractItemModel *model)
Sets the model for the view to present.
ScrollMode horizontalScrollMode
how the view scrolls its contents in the horizontal direction
virtual void setRootIndex(const QModelIndex &index)
Sets the root item to the item at the given index.
virtual void initViewItemOption(QStyleOptionViewItem *option) const
virtual void doItemsLayout()
void update(const QModelIndex &index)
CursorAction
This enum describes the different ways to navigate between items,.
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous)
This slot is called when a new item becomes the current item.
ScrollHint
\value EnsureVisible Scroll to ensure that the item is visible.
virtual void selectAll()
Selects all items in the view.
virtual void updateEditorGeometries()
QItemSelectionModel * selectionModel() const
Returns the current selection model.
virtual void updateGeometries()
virtual void horizontalScrollbarAction(int action)
\inmodule QtCore
Definition qbitarray.h:13
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromRgba(QRgb rgba) noexcept
Static convenience function that returns a QColor constructed from the given QRgb value rgba.
Definition qcolor.cpp:2385
\inmodule QtCore
The QHeaderView class provides a header row or header column for item views.
Definition qheaderview.h:18
void geometriesChanged()
void setSortIndicatorShown(bool show)
void setHighlightSections(bool highlight)
bool sectionsHidden() const
void setSectionsClickable(bool clickable)
Set \l sectionsClickable to clickable.
int sectionViewportPosition(int logicalIndex) const
Returns the section viewport position of the given logicalIndex.
bool isSectionHidden(int logicalIndex) const
Returns true if the section specified by logicalIndex is explicitly hidden from the user; otherwise r...
void sectionCountChanged(int oldCount, int newCount)
This signal is emitted when the number of sections changes, i.e., when sections are added or deleted.
void sectionResized(int logicalIndex, int oldSize, int newSize)
This signal is emitted when a section is resized.
int sectionSize(int logicalIndex) const
Returns the width (or height for vertical headers) of the given logicalIndex.
bool sectionsMoved() const
Returns true if sections in the header has been moved; otherwise returns false;.
void sectionPressed(int logicalIndex)
This signal is emitted when a section is pressed.
void sortIndicatorChanged(int logicalIndex, Qt::SortOrder order)
int logicalIndexAt(int position) const
Returns the section that covers the given position in the viewport.
int logicalIndex(int visualIndex) const
Returns the logicalIndex for the section at the given visualIndex position, or -1 if visualIndex < 0 ...
int length() const
Returns the length along the orientation of the header.
void sectionHandleDoubleClicked(int logicalIndex)
This signal is emitted when a section is double-clicked.
int visualIndexAt(int position) const
Returns the visual index of the section that covers the given position in the viewport.
void sectionEntered(int logicalIndex)
int visualIndex(int logicalIndex) const
Returns the visual index position of the section specified by the given logicalIndex,...
void sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
This signal is emitted when a section is moved.
int offset() const
Returns the offset of the header: this is the header's left-most (or top-most for vertical headers) v...
int count() const
Returns the number of sections in the header.
Q_INVOKABLE QModelIndexList selectedRows(int column=0) const
Q_INVOKABLE bool isSelected(const QModelIndex &index) const
Returns true if the given model item index is selected.
virtual void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
Sets the model item index to be the current item, and emits currentChanged().
virtual void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
Selects the model item index using the specified command, and emits selectionChanged().
void currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
This signal is emitted if the current item changes and its row is different to the row of the previou...
Q_INVOKABLE QModelIndexList selectedColumns(int row=0) const
\inmodule QtCore
Q_CORE_EXPORT QModelIndexList indexes() const
Returns a list of model indexes that correspond to the selected items.
\inmodule QtCore\compares equality \compareswith equality QLine \endcompareswith
Definition qline.h:192
bool isEmpty() const noexcept
Definition qlist.h:401
void reserve(qsizetype size)
Definition qlist.h:753
void append(parameter_type t)
Definition qlist.h:458
Definition qmap.h:187
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
\inmodule QtCore
constexpr int row() const noexcept
Returns the row this model index refers to.
constexpr const QAbstractItemModel * model() const noexcept
Returns a pointer to the model containing the item that this index refers to.
constexpr int column() const noexcept
Returns the column this model index refers to.
constexpr bool isValid() const noexcept
Returns {true} if this model index is valid; otherwise returns {false}.
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
The QPaintEvent class contains event parameters for paint events.
Definition qevent.h:486
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
const QPen & pen() const
Returns the painter's current pen.
void setPen(const QColor &color)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void drawLine(const QLineF &line)
Draws a line defined by line.
Definition qpainter.h:442
void setClipRegion(const QRegion &, Qt::ClipOperation op=Qt::ReplaceClip)
Sets the clip region to the given region using the specified clip operation.
void setCurrentColorGroup(ColorGroup cg)
Set the palette's current color group to cg.
Definition qpalette.h:65
ColorGroup
\value Disabled \value Active \value Inactive \value Normal synonym for Active
Definition qpalette.h:49
@ Disabled
Definition qpalette.h:49
\inmodule QtGui
Definition qpen.h:28
int column() const
Returns the column this persistent model index refers to.
int row() const
Returns the row this persistent model index refers to.
\inmodule QtCore\reentrant
Definition qpoint.h:25
T * data() const noexcept
Definition qpointer.h:73
\inmodule QtCore\reentrant
Definition qrect.h:30
bool intersects(const QRect &r) const noexcept
Returns true if this rectangle intersects with the given rectangle (i.e., there is at least one pixel...
Definition qrect.cpp:1069
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
Definition qrect.h:164
QRect intersected(const QRect &other) const noexcept
Definition qrect.h:415
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:176
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:173
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr int right() const noexcept
Returns the x-coordinate of the rectangle's right edge.
Definition qrect.h:179
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
QRegion translated(int dx, int dy) const
Definition qregion.cpp:593
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
iterator erase(const_iterator i)
Definition qset.h:145
iterator find(const T &value)
Definition qset.h:159
bool contains(const T &value) const
Definition qset.h:71
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
void addSpan(Span *span)
std::list< Span * > SpanList
QSet< Span * > spansInRect(int x, int y, int w, int h) const
void updateRemovedColumns(int start, int end)
void updateInsertedColumns(int start, int end)
void updateSpan(Span *span, int old_height)
Span * spanAt(int x, int y) const
void updateRemovedRows(int start, int end)
void updateInsertedRows(int start, int end)
QString left(qsizetype n) const &
Definition qstring.h:363
QString right(qsizetype n) const &
Definition qstring.h:375
ButtonFeatures features
The QStyleOptionHeader class is used to describe the parameters for drawing a header.
QStyle::State state
QPalette palette
void initFrom(const QWidget *w)
The QStyle class is an abstract base class that encapsulates the look and feel of a GUI.
Definition qstyle.h:29
@ State_MouseOver
Definition qstyle.h:80
@ State_Sunken
Definition qstyle.h:69
@ State_HasFocus
Definition qstyle.h:75
@ State_Active
Definition qstyle.h:83
@ State_Enabled
Definition qstyle.h:67
@ State_Selected
Definition qstyle.h:82
@ State_None
Definition qstyle.h:66
@ SH_Table_GridLineColor
Definition qstyle.h:619
@ SH_Table_AlwaysDrawLeftTopGridLines
Definition qstyle.h:705
@ CE_Header
Definition qstyle.h:202
@ PE_PanelItemViewRow
Definition qstyle.h:154
QRect visualSpanRect(const QSpanCollection::Span &span) const
QSpanCollection spans
QMetaObject::Connection selectionmodelConnection
int logicalColumn(int visualCol) const
bool hasSpans() const
void drawAndClipSpans(const QRegion &area, QPainter *painter, const QStyleOptionViewItem &option, QBitArray *drawn, int firstVisualRow, int lastVisualRow, int firstVisualColumn, int lastVisualColumn)
void updateSpanInsertedRows(const QModelIndex &parent, int start, int end)
QHeaderView * horizontalHeader
std::array< QMetaObject::Connection, 7 > verHeaderConnections
std::vector< QMetaObject::Connection > dynHorHeaderConnections
bool isRowHidden(int row) const
bool isCellEnabled(int row, int column) const
bool isColumnHidden(int column) const
int sectionSpanSize(const QHeaderView *header, int logical, int span) const
int nextActiveVisualColumn(int row, int columnToStart, int limit, SearchDirection searchDirection) const
int columnSpan(int row, int column) const
std::array< QMetaObject::Connection, 4 > modelConnections
void updateSpanRemovedColumns(const QModelIndex &parent, int start, int end)
void selectColumn(int column, bool anchor)
void trimHiddenSelections(QItemSelectionRange *range) const
int logicalRow(int visualRow) const
QSpanCollection::Span span(int row, int column) const
void drawCell(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index)
int visualRow(int logicalRow) const
int widthHintForIndex(const QModelIndex &index, int hint, const QStyleOptionViewItem &option) const
QHeaderView * verticalHeader
bool spanContainsSection(const QHeaderView *header, int logical, int spanLogical, int span) const
int heightHintForIndex(const QModelIndex &index, int hint, QStyleOptionViewItem &option) const
int rowSpanHeight(int row, int span) const
int nextActiveVisualRow(int rowToStart, int column, int limit, SearchDirection searchDirection) const
void setSpan(int row, int column, int rowSpan, int columnSpan)
int columnSpanWidth(int column, int span) const
int sectionSpanEndLogical(const QHeaderView *header, int logical, int span) const
void updateSpanRemovedRows(const QModelIndex &parent, int start, int end)
void selectRow(int row, bool anchor)
void updateSpanInsertedColumns(const QModelIndex &parent, int start, int end)
int rowSpan(int row, int column) const
QRect intersectedRect(const QRect rect, const QModelIndex &topLeft, const QModelIndex &bottomRight) const override
std::array< QMetaObject::Connection, 5 > horHeaderConnections
int visualColumn(int logicalCol) const
void sortIndicatorChanged(int column, Qt::SortOrder order)
The QTableView class provides a default model/view implementation of a table view.
Definition qtableview.h:18
void setModel(QAbstractItemModel *model) override
\reimp
int rowAt(int y) const
Returns the row in which the given y-coordinate, y, in contents coordinates is located.
void verticalScrollbarAction(int action) override
bool isColumnHidden(int column) const
Returns true if the given column is hidden; otherwise returns false.
void setHorizontalHeader(QHeaderView *header)
Sets the widget to use for the horizontal header to header.
QTableView(QWidget *parent=nullptr)
Constructs a table view with a parent to represent the data.
int rowSpan(int row, int column) const
void scrollTo(const QModelIndex &index, ScrollHint hint=EnsureVisible) override
\reimp
void columnCountChanged(int oldCount, int newCount)
This slot is called whenever columns are added or deleted.
void setSortingEnabled(bool enable)
If enable is true, enables sorting for the table and immediately trigger a call to sortByColumn() wit...
bool showGrid
whether the grid is shown
Definition qtableview.h:20
void columnResized(int column, int oldWidth, int newWidth)
This slot is called to change the width of the given column.
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override
\reimp
void setRootIndex(const QModelIndex &index) override
\reimp
void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) override
Selects the items within the given rect and in accordance with the specified selection flags.
int columnViewportPosition(int column) const
Returns the x-coordinate in contents coordinates of the given column.
void setVerticalHeader(QHeaderView *header)
Sets the widget to use for the vertical header to header.
QRect visualRect(const QModelIndex &index) const override
\reimp
void sortByColumn(int column, Qt::SortOrder order)
QModelIndex indexAt(const QPoint &p) const override
Returns the index position of the model item corresponding to the table item at position pos in conte...
void resizeColumnsToContents()
Resizes all columns based on the size hints of the delegate used to render each item in the columns.
void initViewItemOption(QStyleOptionViewItem *option) const override
\reimp
int columnWidth(int column) const
Returns the width of the given column.
void setColumnHidden(int column, bool hide)
If hide is true the given column will be hidden; otherwise it will be shown.
int rowHeight(int row) const
Returns the height of the given row.
void setRowHidden(int row, bool hide)
If hide is true row will be hidden, otherwise it will be shown.
void columnMoved(int column, int oldIndex, int newIndex)
This slot is called to change the index of the given column in the table view.
int sizeHintForColumn(int column) const override
Returns the size hint for the given column's width or -1 if there is no model.
bool isRowHidden(int row) const
Returns true if the given row is hidden; otherwise returns false.
void showRow(int row)
Show the given row.
void rowMoved(int row, int oldIndex, int newIndex)
This slot is called to change the index of the given row in the table view.
void scrollContentsBy(int dx, int dy) override
\reimp
void setWordWrap(bool on)
void horizontalScrollbarAction(int action) override
void paintEvent(QPaintEvent *e) override
Paints the table on receipt of the given paint event event.
bool isSortingEnabled() const
Qt::PenStyle gridStyle
the pen style used to draw the grid.
Definition qtableview.h:21
QSize viewportSizeHint() const override
\reimp
void setColumnWidth(int column, int width)
void setShowGrid(bool show)
void selectColumn(int column)
Selects the given column in the table view if the current SelectionMode and SelectionBehavior allows ...
int sizeHintForRow(int row) const override
Returns the size hint for the given row's height or -1 if there is no model.
void selectRow(int row)
Selects the given row in the table view if the current SelectionMode and SelectionBehavior allows row...
void clearSpans()
void hideRow(int row)
Hide the given row.
QRegion visualRegionForSelection(const QItemSelection &selection) const override
\reimp
void updateGeometries() override
\reimp
QHeaderView * horizontalHeader() const
Returns the table view's horizontal header.
int verticalOffset() const override
Returns the vertical offset of the items in the table view.
void showColumn(int column)
Show the given column.
QModelIndexList selectedIndexes() const override
\reimp
bool wordWrap
the item text word-wrapping policy
Definition qtableview.h:23
void rowResized(int row, int oldHeight, int newHeight)
This slot is called to change the height of the given row.
void setSelectionModel(QItemSelectionModel *selectionModel) override
\reimp
int columnSpan(int row, int column) const
void resizeColumnToContents(int column)
Resizes the given column based on the size hints of the delegate used to render each item in the colu...
QHeaderView * verticalHeader() const
Returns the table view's vertical header.
void doItemsLayout() override
void timerEvent(QTimerEvent *event) override
\reimp
~QTableView()
Destroys the table view.
int columnAt(int x) const
Returns the column in which the given x-coordinate, x, in contents coordinates is located.
int rowViewportPosition(int row) const
Returns the y-coordinate in contents coordinates of the given row.
void setSpan(int row, int column, int rowSpan, int columnSpan)
bool isIndexHidden(const QModelIndex &index) const override
\reimp
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override
\reimp
void hideColumn(int column)
Hide the given column.
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
Moves the cursor in accordance with the given cursorAction, using the information provided by the mod...
void rowCountChanged(int oldCount, int newCount)
This slot is called whenever rows are added or deleted.
void resizeRowToContents(int row)
Resizes the given row based on the size hints of the delegate used to render each item in the row.
void setGridStyle(Qt::PenStyle style)
void setRowHeight(int row, int height)
void resizeRowsToContents()
Resizes all rows based on the size hints of the delegate used to render each item in the rows.
int horizontalOffset() const override
Returns the horizontal offset of the items in the table view.
\inmodule QtCore
Definition qcoreevent.h:366
The QWidget class is the base class of all user interface objects.
Definition qwidget.h:99
QSize minimumSize
the widget's minimum size
Definition qwidget.h:120
QSize maximumSize
the widget's maximum size in pixels
Definition qwidget.h:121
QSize sizeHint
the recommended size for the widget
Definition qwidget.h:148
EGLImageKHR int int EGLuint64KHR * modifiers
QString str
[2]
bool focus
[0]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
rect
[4]
QStyleOptionButton opt
else opt state
[0]
Combined button and popup list for selecting options.
bool isEnabled()
@ NoFocus
Definition qnamespace.h:107
@ Horizontal
Definition qnamespace.h:99
@ Vertical
Definition qnamespace.h:100
SortOrder
Definition qnamespace.h:121
@ ControlModifier
@ ItemIsEnabled
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
#define Q_UNLIKELY(x)
DBusConnection * connection
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static int area(const QSize &s)
Definition qicon.cpp:153
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLsizei range
GLint GLsizei width
GLint left
GLint GLint bottom
GLboolean enable
GLuint start
GLenum GLuint GLintptr offset
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLenum GLenum GLsizei void GLsizei void * column
struct _cl_event * event
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint entry
GLint limit
GLdouble GLdouble t
Definition qopenglext.h:243
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLenum GLenum GLsizei void GLsizei void void * span
GLuint64EXT * result
[6]
GLuint GLenum option
GLfixed GLfixed GLint GLint order
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define sp
#define Q_OBJECT
#define Q_UNUSED(x)
QSqlQueryModel * model
[16]
view show()
[18] //! [19]
QList< int > list
[14]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
myObject disconnect()
[26]
view viewport() -> scroll(dx, dy, deviceRect)
edit hide()
edit isVisible()
QItemSelection * selection
[0]
QPainter painter(this)
[7]
QPointer< QWidget > widget
bool contains(const AT &t) const noexcept
Definition qlist.h:45
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...