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 &current, 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