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