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