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