1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qheaderview.h"
41 
42 #include <qbitarray.h>
43 #include <qbrush.h>
44 #include <qdebug.h>
45 #include <qevent.h>
46 #include <qpainter.h>
47 #include <qscrollbar.h>
48 #include <qtooltip.h>
49 #if QT_CONFIG(whatsthis)
50 #include <qwhatsthis.h>
51 #endif
52 #include <qstyle.h>
53 #include <qstyleoption.h>
54 #include <qvector.h>
55 #include <qapplication.h>
56 #include <qvarlengtharray.h>
57 #include <qabstractitemdelegate.h>
58 #include <qvariant.h>
59 #include <private/qheaderview_p.h>
60 #include <private/qabstractitemmodel_p.h>
61 
62 #ifndef QT_NO_DATASTREAM
63 #include <qdatastream.h>
64 #endif
65 
66 QT_BEGIN_NAMESPACE
67 
68 #ifndef QT_NO_DATASTREAM
operator <<(QDataStream & out,const QHeaderViewPrivate::SectionItem & section)69 QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionItem &section)
70 {
71     section.write(out);
72     return out;
73 }
74 
operator >>(QDataStream & in,QHeaderViewPrivate::SectionItem & section)75 QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionItem &section)
76 {
77     section.read(in);
78     return in;
79 }
80 #endif // QT_NO_DATASTREAM
81 
82 static const int maxSizeSection = 1048575; // since section size is in a bitfield (uint 20). See qheaderview_p.h
83                                            // if this is changed then the docs in maximumSectionSize should be changed.
84 
85 /*!
86     \class QHeaderView
87 
88     \brief The QHeaderView class provides a header row or header column for
89     item views.
90 
91     \ingroup model-view
92     \inmodule QtWidgets
93 
94     A QHeaderView displays the headers used in item views such as the
95     QTableView and QTreeView classes. It takes the place of Qt3's \c QHeader
96     class previously used for the same purpose, but uses the Qt's model/view
97     architecture for consistency with the item view classes.
98 
99     The QHeaderView class is one of the \l{Model/View Classes} and is part of
100     Qt's \l{Model/View Programming}{model/view framework}.
101 
102     The header gets the data for each section from the model using the
103     QAbstractItemModel::headerData() function. You can set the data by using
104     QAbstractItemModel::setHeaderData().
105 
106     Each header has an orientation() and a number of sections, given by the
107     count() function. A section refers to a part of the header - either a row
108     or a column, depending on the orientation.
109 
110     Sections can be moved and resized using moveSection() and resizeSection();
111     they can also be hidden and shown with hideSection() and showSection().
112 
113     Each section of a header is described by a section ID, specified by its
114     section(), and can be located at a particular visualIndex() in the header.
115     A section can have a sort indicator set with setSortIndicator(); this
116     indicates whether the items in the associated item view will be sorted in
117     the order given by the section.
118 
119     For a horizontal header the section is equivalent to a column in the model,
120     and for a vertical header the section is equivalent to a row in the model.
121 
122     \section1 Moving Header Sections
123 
124     A header can be fixed in place, or made movable with setSectionsMovable(). It can
125     be made clickable with setSectionsClickable(), and has resizing behavior in
126     accordance with setSectionResizeMode().
127 
128     \note Double-clicking on a header to resize a section only applies for
129     visible rows.
130 
131     A header will emit sectionMoved() if the user moves a section,
132     sectionResized() if the user resizes a section, and sectionClicked() as
133     well as sectionHandleDoubleClicked() in response to mouse clicks. A header
134     will also emit sectionCountChanged().
135 
136     You can identify a section using the logicalIndex() and logicalIndexAt()
137     functions, or by its index position, using the visualIndex() and
138     visualIndexAt() functions. The visual index will change if a section is
139     moved, but the logical index will not change.
140 
141     \section1 Appearance
142 
143     QTableWidget and QTableView create default headers. If you want
144     the headers to be visible, you can use \l{QFrame::}{setVisible()}.
145 
146     Not all \l{Qt::}{ItemDataRole}s will have an effect on a
147     QHeaderView. If you need to draw other roles, you can subclass
148     QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
149     QHeaderView respects the following item data roles, unless they are
150     in conflict with the style (which can happen for styles that follow
151     the desktop theme):
152 
153     \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
154     \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
155     \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
156 
157     \note Each header renders the data for each section itself, and does not
158     rely on a delegate. As a result, calling a header's setItemDelegate()
159     function will have no effect.
160 
161     \sa {Model/View Programming}, QListView, QTableView, QTreeView
162 */
163 
164 /*!
165     \enum QHeaderView::ResizeMode
166 
167     The resize mode specifies the behavior of the header sections. It can be
168     set on the entire header view or on individual sections using
169     setSectionResizeMode().
170 
171     \value Interactive The user can resize the section. The section can also be
172            resized programmatically using resizeSection().  The section size
173            defaults to \l defaultSectionSize. (See also
174            \l cascadingSectionResizes.)
175 
176     \value Fixed The user cannot resize the section. The section can only be
177            resized programmatically using resizeSection(). The section size
178            defaults to \l defaultSectionSize.
179 
180     \value Stretch QHeaderView will automatically resize the section to fill
181            the available space. The size cannot be changed by the user or
182            programmatically.
183 
184     \value ResizeToContents QHeaderView will automatically resize the section
185            to its optimal size based on the contents of the entire column or
186            row. The size cannot be changed by the user or programmatically.
187            (This value was introduced in 4.2)
188 
189     The following values are obsolete:
190     \value Custom Use Fixed instead.
191 
192     \sa setSectionResizeMode(), stretchLastSection, minimumSectionSize
193 */
194 
195 /*!
196     \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
197     int newVisualIndex)
198 
199     This signal is emitted when a section is moved. The section's logical index
200     is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
201     the new index position by \a newVisualIndex.
202 
203     \sa moveSection()
204 */
205 
206 /*!
207     \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
208     int newSize)
209 
210     This signal is emitted when a section is resized. The section's logical
211     number is specified by \a logicalIndex, the old size by \a oldSize, and the
212     new size by \a newSize.
213 
214     \sa resizeSection()
215 */
216 
217 /*!
218     \fn void QHeaderView::sectionPressed(int logicalIndex)
219 
220     This signal is emitted when a section is pressed. The section's logical
221     index is specified by \a logicalIndex.
222 
223     \sa setSectionsClickable()
224 */
225 
226 /*!
227     \fn void QHeaderView::sectionClicked(int logicalIndex)
228 
229     This signal is emitted when a section is clicked. The section's logical
230     index is specified by \a logicalIndex.
231 
232     Note that the sectionPressed signal will also be emitted.
233 
234     \sa setSectionsClickable(), sectionPressed()
235 */
236 
237 /*!
238     \fn void QHeaderView::sectionEntered(int logicalIndex)
239     \since 4.3
240 
241     This signal is emitted when the cursor moves over the section and the left
242     mouse button is pressed. The section's logical index is specified by
243     \a logicalIndex.
244 
245     \sa setSectionsClickable(), sectionPressed()
246 */
247 
248 /*!
249     \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
250 
251     This signal is emitted when a section is double-clicked. The section's
252     logical index is specified by \a logicalIndex.
253 
254     \sa setSectionsClickable()
255 */
256 
257 /*!
258     \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
259 
260     This signal is emitted when the number of sections changes, i.e., when
261     sections are added or deleted. The original count is specified by
262     \a oldCount, and the new count by \a newCount.
263 
264     \sa count(), length(), headerDataChanged()
265 */
266 
267 /*!
268     \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
269 
270     This signal is emitted when a section is double-clicked. The section's
271     logical index is specified by \a logicalIndex.
272 
273     \sa setSectionsClickable()
274 */
275 
276 /*!
277     \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
278     Qt::SortOrder order)
279     \since 4.3
280 
281     This signal is emitted when the section containing the sort indicator or
282     the order indicated is changed. The section's logical index is specified
283     by \a logicalIndex and the sort order is specified by \a order.
284 
285     \sa setSortIndicator()
286 */
287 
288 /*!
289     \fn void QHeaderView::geometriesChanged()
290     \since 4.2
291 
292     This signal is emitted when the header's geometries have changed.
293 */
294 
295 /*!
296     \property QHeaderView::highlightSections
297     \brief whether the sections containing selected items are highlighted
298 
299     By default, this property is \c false.
300 */
301 
302 /*!
303     Creates a new generic header with the given \a orientation and \a parent.
304 */
QHeaderView(Qt::Orientation orientation,QWidget * parent)305 QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
306     : QAbstractItemView(*new QHeaderViewPrivate, parent)
307 {
308     Q_D(QHeaderView);
309     d->setDefaultValues(orientation);
310     initialize();
311 }
312 
313 /*!
314   \internal
315 */
QHeaderView(QHeaderViewPrivate & dd,Qt::Orientation orientation,QWidget * parent)316 QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
317                          Qt::Orientation orientation, QWidget *parent)
318     : QAbstractItemView(dd, parent)
319 {
320     Q_D(QHeaderView);
321     d->setDefaultValues(orientation);
322     initialize();
323 }
324 
325 /*!
326   Destroys the header.
327 */
328 
~QHeaderView()329 QHeaderView::~QHeaderView()
330 {
331 }
332 
333 /*!
334   \internal
335 */
initialize()336 void QHeaderView::initialize()
337 {
338     Q_D(QHeaderView);
339     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
340     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
341     setFrameStyle(NoFrame);
342     setFocusPolicy(Qt::NoFocus);
343     d->viewport->setMouseTracking(true);
344     d->viewport->setBackgroundRole(QPalette::Button);
345     d->textElideMode = Qt::ElideNone;
346     delete d->itemDelegate;
347 }
348 
349 /*!
350   \reimp
351 */
setModel(QAbstractItemModel * model)352 void QHeaderView::setModel(QAbstractItemModel *model)
353 {
354     if (model == this->model())
355         return;
356     Q_D(QHeaderView);
357     d->layoutChangePersistentSections.clear();
358     if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
359         if (d->orientation == Qt::Horizontal) {
360             QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
361                                 this, SLOT(sectionsInserted(QModelIndex,int,int)));
362             QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
363                                 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
364             QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
365                                 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
366             QObject::disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
367                                 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
368             QObject::disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
369                                 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
370         } else {
371             QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
372                                 this, SLOT(sectionsInserted(QModelIndex,int,int)));
373             QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
374                                 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
375             QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
376                                 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
377             QObject::disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
378                                 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
379             QObject::disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
380                                 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
381         }
382         QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
383                             this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
384         QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
385                             this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
386         QObject::disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
387                             this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
388     }
389 
390     if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
391         if (d->orientation == Qt::Horizontal) {
392             QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
393                              this, SLOT(sectionsInserted(QModelIndex,int,int)));
394             QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
395                              this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
396             QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
397                              this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
398             QObject::connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
399                              this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
400             QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
401                              this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
402         } else {
403             QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
404                              this, SLOT(sectionsInserted(QModelIndex,int,int)));
405             QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
406                              this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
407             QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
408                              this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
409             QObject::connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
410                              this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
411             QObject::connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
412                              this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
413         }
414         QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
415                          this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
416         QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
417                          this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
418         QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
419                          this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
420     }
421 
422     d->state = QHeaderViewPrivate::NoClear;
423     QAbstractItemView::setModel(model);
424     d->state = QHeaderViewPrivate::NoState;
425 
426     // Users want to set sizes and modes before the widget is shown.
427     // Thus, we have to initialize when the model is set,
428     // and not lazily like we do in the other views.
429     initializeSections();
430 }
431 
432 /*!
433     Returns the orientation of the header.
434 
435     \sa Qt::Orientation
436 */
437 
orientation() const438 Qt::Orientation QHeaderView::orientation() const
439 {
440     Q_D(const QHeaderView);
441     return d->orientation;
442 }
443 
444 /*!
445     Returns the offset of the header: this is the header's left-most (or
446     top-most for vertical headers) visible pixel.
447 
448     \sa setOffset()
449 */
450 
offset() const451 int QHeaderView::offset() const
452 {
453     Q_D(const QHeaderView);
454     return d->offset;
455 }
456 
457 /*!
458     \fn void QHeaderView::setOffset(int offset)
459 
460     Sets the header's offset to \a offset.
461 
462     \sa offset(), length()
463 */
464 
setOffset(int newOffset)465 void QHeaderView::setOffset(int newOffset)
466 {
467     Q_D(QHeaderView);
468     if (d->offset == (int)newOffset)
469         return;
470     int ndelta = d->offset - newOffset;
471     d->offset = newOffset;
472     if (d->orientation == Qt::Horizontal)
473         d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
474     else
475         d->viewport->scroll(0, ndelta);
476     if (d->state == QHeaderViewPrivate::ResizeSection && !d->preventCursorChangeInSetOffset) {
477         QPoint cursorPos = QCursor::pos();
478         if (d->orientation == Qt::Horizontal)
479             QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
480         else
481             QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
482         d->firstPos += ndelta;
483         d->lastPos += ndelta;
484     }
485 }
486 
487 /*!
488     \since 4.2
489     Sets the offset to the start of the section at the given \a visualSectionNumber.
490     \a visualSectionNumber is the actual visible section when hiddenSections are
491     not considered. That is not always the same as visualIndex().
492 
493     \sa setOffset(), sectionPosition()
494 */
setOffsetToSectionPosition(int visualSectionNumber)495 void QHeaderView::setOffsetToSectionPosition(int visualSectionNumber)
496 {
497     Q_D(QHeaderView);
498     if (visualSectionNumber > -1 && visualSectionNumber < d->sectionCount()) {
499         int position = d->headerSectionPosition(d->adjustedVisualIndex(visualSectionNumber));
500         setOffset(position);
501     }
502 }
503 
504 /*!
505     \since 4.2
506     Sets the offset to make the last section visible.
507 
508     \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
509 */
setOffsetToLastSection()510 void QHeaderView::setOffsetToLastSection()
511 {
512     Q_D(const QHeaderView);
513     int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
514     int position = length() - size;
515     setOffset(position);
516 }
517 
518 /*!
519     Returns the length along the orientation of the header.
520 
521     \sa sizeHint(), setSectionResizeMode(), offset()
522 */
523 
length() const524 int QHeaderView::length() const
525 {
526     Q_D(const QHeaderView);
527     d->executePostedLayout();
528     d->executePostedResize();
529     //Q_ASSERT(d->headerLength() == d->length);
530     return d->length;
531 }
532 
533 /*!
534     Returns a suitable size hint for this header.
535 
536     \sa sectionSizeHint()
537 */
538 
sizeHint() const539 QSize QHeaderView::sizeHint() const
540 {
541     Q_D(const QHeaderView);
542     if (d->cachedSizeHint.isValid())
543         return d->cachedSizeHint;
544     d->cachedSizeHint = QSize(0, 0); //reinitialize the cached size hint
545     const int sectionCount = count();
546 
547     // get size hint for the first n sections
548     int i = 0;
549     for (int checked = 0; checked < 100 && i < sectionCount; ++i) {
550         if (isSectionHidden(i))
551             continue;
552         checked++;
553         QSize hint = sectionSizeFromContents(i);
554         d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
555     }
556     // get size hint for the last n sections
557     i = qMax(i, sectionCount - 100 );
558     for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) {
559         if (isSectionHidden(j))
560             continue;
561         checked++;
562         QSize hint = sectionSizeFromContents(j);
563         d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
564     }
565     return d->cachedSizeHint;
566 }
567 
568 /*!
569     \reimp
570 */
571 
setVisible(bool v)572 void QHeaderView::setVisible(bool v)
573 {
574     bool actualChange = (v != isVisible());
575     QAbstractItemView::setVisible(v);
576     if (actualChange) {
577         QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget());
578         if (parent)
579             parent->updateGeometry();
580     }
581 }
582 
583 
584 /*!
585     Returns a suitable size hint for the section specified by \a logicalIndex.
586 
587     \sa sizeHint(), defaultSectionSize(), minimumSectionSize(), maximumSectionSize()
588     Qt::SizeHintRole
589 */
590 
sectionSizeHint(int logicalIndex) const591 int QHeaderView::sectionSizeHint(int logicalIndex) const
592 {
593     Q_D(const QHeaderView);
594     if (isSectionHidden(logicalIndex))
595         return 0;
596     if (logicalIndex < 0 || logicalIndex >= count())
597         return -1;
598     QSize size;
599     QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
600     if (value.isValid())
601         size = qvariant_cast<QSize>(value);
602     else
603         size = sectionSizeFromContents(logicalIndex);
604     int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
605     return qBound(minimumSectionSize(), hint, maximumSectionSize());
606 }
607 
608 /*!
609     Returns the visual index of the section that covers the given \a position
610     in the viewport.
611 
612     \sa logicalIndexAt()
613 */
614 
visualIndexAt(int position) const615 int QHeaderView::visualIndexAt(int position) const
616 {
617     Q_D(const QHeaderView);
618     int vposition = position;
619     d->executePostedLayout();
620     d->executePostedResize();
621     const int count = d->sectionCount();
622     if (count < 1)
623         return -1;
624 
625     if (d->reverse())
626         vposition = d->viewport->width() - vposition - 1;
627     vposition += d->offset;
628 
629     if (vposition > d->length)
630         return -1;
631     int visual = d->headerVisualIndexAt(vposition);
632     if (visual < 0)
633         return -1;
634 
635     while (d->isVisualIndexHidden(visual)){
636         ++visual;
637         if (visual >= count)
638             return -1;
639     }
640     return visual;
641 }
642 
643 /*!
644     Returns the section that covers the given \a position in the viewport.
645 
646     \sa visualIndexAt(), isSectionHidden()
647 */
648 
logicalIndexAt(int position) const649 int QHeaderView::logicalIndexAt(int position) const
650 {
651     const int visual = visualIndexAt(position);
652     if (visual > -1)
653         return logicalIndex(visual);
654     return -1;
655 }
656 
657 /*!
658     Returns the width (or height for vertical headers) of the given
659     \a logicalIndex.
660 
661     \sa length(), setSectionResizeMode(), defaultSectionSize()
662 */
663 
sectionSize(int logicalIndex) const664 int QHeaderView::sectionSize(int logicalIndex) const
665 {
666     Q_D(const QHeaderView);
667     if (isSectionHidden(logicalIndex))
668         return 0;
669     if (logicalIndex < 0 || logicalIndex >= count())
670         return 0;
671     int visual = visualIndex(logicalIndex);
672     if (visual == -1)
673         return 0;
674     d->executePostedResize();
675     return d->headerSectionSize(visual);
676 }
677 
678 /*!
679 
680     Returns the section position of the given \a logicalIndex, or -1
681     if the section is hidden. The position is measured in pixels from
682     the first visible item's top-left corner to the top-left corner of
683     the item with \a logicalIndex. The measurement is along the x-axis
684     for horizontal headers and along the y-axis for vertical headers.
685 
686     \sa sectionViewportPosition()
687 */
688 
sectionPosition(int logicalIndex) const689 int QHeaderView::sectionPosition(int logicalIndex) const
690 {
691     Q_D(const QHeaderView);
692     int visual = visualIndex(logicalIndex);
693     // in some cases users may change the selections
694     // before we have a chance to do the layout
695     if (visual == -1)
696         return -1;
697     d->executePostedResize();
698     return d->headerSectionPosition(visual);
699 }
700 
701 /*!
702     Returns the section viewport position of the given \a logicalIndex.
703 
704     If the section is hidden, the return value is undefined.
705 
706     \sa sectionPosition(), isSectionHidden()
707 */
708 
sectionViewportPosition(int logicalIndex) const709 int QHeaderView::sectionViewportPosition(int logicalIndex) const
710 {
711     Q_D(const QHeaderView);
712     if (logicalIndex >= count())
713         return -1;
714     int position = sectionPosition(logicalIndex);
715     if (position < 0)
716         return position; // the section was hidden
717     int offsetPosition = position - d->offset;
718     if (d->reverse())
719         return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
720     return offsetPosition;
721 }
722 
723 /*!
724     \fn int QHeaderView::logicalIndexAt(int x, int y) const
725 
726     Returns the logical index of the section at the given coordinate. If the
727     header is horizontal \a x will be used, otherwise \a y will be used to
728     find the logical index.
729 */
730 
731 /*!
732     \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
733 
734     Returns the logical index of the section at the position given in \a pos.
735     If the header is horizontal the x-coordinate will be used, otherwise the
736     y-coordinate will be used to find the logical index.
737 
738     \sa sectionPosition()
739 */
740 
741 template<typename Container>
qMoveRange(Container & c,typename Container::size_type rangeStart,typename Container::size_type rangeEnd,typename Container::size_type targetPosition)742 static void qMoveRange(Container& c,
743                typename Container::size_type rangeStart,
744                typename Container::size_type rangeEnd,
745                typename Container::size_type targetPosition)
746 {
747     Q_ASSERT(targetPosition <= c.size());
748     Q_ASSERT(targetPosition < rangeStart || targetPosition >= rangeEnd);
749 
750     const bool forwardMove = targetPosition > rangeStart;
751     typename Container::size_type first = std::min(rangeStart, targetPosition);
752     typename Container::size_type mid = forwardMove ? rangeEnd : rangeStart;
753     typename Container::size_type last = forwardMove ? targetPosition + 1 : rangeEnd;
754     std::rotate(c.begin() + first, c.begin() + mid, c.begin() + last);
755 }
756 
757 /*!
758     Moves the section at visual index \a from to occupy visual index \a to.
759 
760     \sa sectionsMoved()
761 */
762 
moveSection(int from,int to)763 void QHeaderView::moveSection(int from, int to)
764 {
765     Q_D(QHeaderView);
766 
767     d->executePostedLayout();
768     if (from < 0 || from >= d->sectionCount() || to < 0 || to >= d->sectionCount())
769         return;
770 
771     if (from == to) {
772         int logical = logicalIndex(from);
773         Q_ASSERT(logical != -1);
774         updateSection(logical);
775         return;
776     }
777 
778     d->initializeIndexMapping();
779 
780     int *visualIndices = d->visualIndices.data();
781     int *logicalIndices = d->logicalIndices.data();
782     int logical = logicalIndices[from];
783     int visual = from;
784 
785     if (to > from) {
786         while (visual < to) {
787             visualIndices[logicalIndices[visual + 1]] = visual;
788             logicalIndices[visual] = logicalIndices[visual + 1];
789             ++visual;
790         }
791     } else {
792         while (visual > to) {
793             visualIndices[logicalIndices[visual - 1]] = visual;
794             logicalIndices[visual] = logicalIndices[visual - 1];
795             --visual;
796         }
797     }
798     visualIndices[logical] = to;
799     logicalIndices[to] = logical;
800 
801     qMoveRange(d->sectionItems, from, from + 1, to);
802 
803     d->sectionStartposRecalc = true;
804 
805     if (d->hasAutoResizeSections())
806         d->doDelayedResizeSections();
807     d->viewport->update();
808 
809     emit sectionMoved(logical, from, to);
810 
811     if (stretchLastSection()) {
812         const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx);
813         if (from >= lastSectionVisualIdx || to >= lastSectionVisualIdx)
814             d->maybeRestorePrevLastSectionAndStretchLast();
815     }
816 }
817 
818 /*!
819     \since 4.2
820     Swaps the section at visual index \a first with the section at visual
821     index \a second.
822 
823     \sa moveSection()
824 */
swapSections(int first,int second)825 void QHeaderView::swapSections(int first, int second)
826 {
827     Q_D(QHeaderView);
828 
829     if (first == second)
830         return;
831     d->executePostedLayout();
832     if (first < 0 || first >= d->sectionCount() || second < 0 || second >= d->sectionCount())
833         return;
834 
835     int firstSize = d->headerSectionSize(first);
836     ResizeMode firstMode = d->headerSectionResizeMode(first);
837     int firstLogical = d->logicalIndex(first);
838 
839     int secondSize = d->headerSectionSize(second);
840     ResizeMode secondMode = d->headerSectionResizeMode(second);
841     int secondLogical = d->logicalIndex(second);
842 
843     if (d->state == QHeaderViewPrivate::ResizeSection)
844         d->preventCursorChangeInSetOffset = true;
845 
846     d->createSectionItems(second, second, firstSize, firstMode);
847     d->createSectionItems(first, first, secondSize, secondMode);
848 
849     d->initializeIndexMapping();
850 
851     d->visualIndices[firstLogical] = second;
852     d->logicalIndices[second] = firstLogical;
853 
854     d->visualIndices[secondLogical] = first;
855     d->logicalIndices[first] = secondLogical;
856 
857     if (!d->hiddenSectionSize.isEmpty()) {
858         bool firstHidden = d->isVisualIndexHidden(first);
859         bool secondHidden = d->isVisualIndexHidden(second);
860         d->setVisualIndexHidden(first, secondHidden);
861         d->setVisualIndexHidden(second, firstHidden);
862     }
863 
864     d->viewport->update();
865     emit sectionMoved(firstLogical, first, second);
866     emit sectionMoved(secondLogical, second, first);
867 
868     if (stretchLastSection()) {
869         const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx);
870         if (first >= lastSectionVisualIdx || second >= lastSectionVisualIdx)
871             d->maybeRestorePrevLastSectionAndStretchLast();
872     }
873 }
874 
875 /*!
876     \fn void QHeaderView::resizeSection(int logicalIndex, int size)
877 
878     Resizes the section specified by \a logicalIndex to \a size measured in
879     pixels. The size parameter must be a value larger or equal to zero. A
880     size equal to zero is however not recommended. In that situation hideSection
881     should be used instead.
882 
883     \sa sectionResized(), sectionSize(), hideSection()
884 */
885 
resizeSection(int logical,int size)886 void QHeaderView::resizeSection(int logical, int size)
887 {
888     Q_D(QHeaderView);
889     if (logical < 0 || logical >= count() || size < 0 || size > maxSizeSection)
890         return;
891 
892     // make sure to not exceed bounds when setting size programmatically
893     if (size > 0)
894         size = qBound(minimumSectionSize(), size, maximumSectionSize());
895 
896     if (isSectionHidden(logical)) {
897         d->hiddenSectionSize.insert(logical, size);
898         return;
899     }
900 
901     int visual = visualIndex(logical);
902     if (visual == -1)
903         return;
904 
905     if (d->state == QHeaderViewPrivate::ResizeSection && !d->cascadingResizing && logical != d->section)
906         d->preventCursorChangeInSetOffset = true;
907 
908     int oldSize = d->headerSectionSize(visual);
909     if (oldSize == size)
910         return;
911 
912     d->executePostedLayout();
913     d->invalidateCachedSizeHint();
914 
915     if (stretchLastSection() && logical == d->lastSectionLogicalIdx)
916         d->lastSectionSize = size;
917 
918     d->createSectionItems(visual, visual, size, d->headerSectionResizeMode(visual));
919 
920     if (!updatesEnabled()) {
921         if (d->hasAutoResizeSections())
922             d->doDelayedResizeSections();
923         emit sectionResized(logical, oldSize, size);
924         return;
925     }
926 
927     int w = d->viewport->width();
928     int h = d->viewport->height();
929     int pos = sectionViewportPosition(logical);
930     QRect r;
931     if (d->orientation == Qt::Horizontal)
932         if (isRightToLeft())
933             r.setRect(0, 0, pos + size, h);
934         else
935             r.setRect(pos, 0, w - pos, h);
936     else
937         r.setRect(0, pos, w, h - pos);
938 
939     if (d->hasAutoResizeSections()) {
940         d->doDelayedResizeSections();
941         r = d->viewport->rect();
942     }
943 
944     // If the parent is a QAbstractScrollArea with QAbstractScrollArea::AdjustToContents
945     // then we want to change the geometry on that widget. Not doing it at once can/will
946     // cause scrollbars flicker as they would be shown at first but then removed.
947     // In the same situation it will also allow shrinking the whole view when stretchLastSection is set
948     // (It is default on QTreeViews - and it wouldn't shrink since the last stretch was made before the
949     // viewport was resized)
950 
951     QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
952     if (parent && parent->sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents)
953         parent->updateGeometry();
954 
955     d->viewport->update(r.normalized());
956     emit sectionResized(logical, oldSize, size);
957 }
958 
959 /*!
960     Resizes the sections according to the given \a mode, ignoring the current
961     resize mode.
962 
963     \sa sectionResized()
964 */
965 
resizeSections(QHeaderView::ResizeMode mode)966 void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
967 {
968     Q_D(QHeaderView);
969     d->resizeSections(mode, true);
970 }
971 
972 /*!
973     \fn void QHeaderView::hideSection(int logicalIndex)
974     Hides the section specified by \a logicalIndex.
975 
976     \sa showSection(), isSectionHidden(), hiddenSectionCount(),
977     setSectionHidden()
978 */
979 
980 /*!
981     \fn void QHeaderView::showSection(int logicalIndex)
982     Shows the section specified by \a logicalIndex.
983 
984     \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
985     setSectionHidden()
986 */
987 
988 /*!
989     Returns \c true if the section specified by \a logicalIndex is explicitly
990     hidden from the user; otherwise returns \c false.
991 
992     \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
993 */
994 
isSectionHidden(int logicalIndex) const995 bool QHeaderView::isSectionHidden(int logicalIndex) const
996 {
997     Q_D(const QHeaderView);
998     d->executePostedLayout();
999     if (d->hiddenSectionSize.isEmpty() || logicalIndex < 0 || logicalIndex >= d->sectionCount())
1000         return false;
1001     int visual = visualIndex(logicalIndex);
1002     Q_ASSERT(visual != -1);
1003     return d->isVisualIndexHidden(visual);
1004 }
1005 
1006 /*!
1007     \since 4.1
1008 
1009     Returns the number of sections in the header that has been hidden.
1010 
1011     \sa setSectionHidden(), isSectionHidden()
1012 */
hiddenSectionCount() const1013 int QHeaderView::hiddenSectionCount() const
1014 {
1015     Q_D(const QHeaderView);
1016     return d->hiddenSectionSize.count();
1017 }
1018 
1019 /*!
1020   If \a hide is true the section specified by \a logicalIndex is hidden;
1021   otherwise the section is shown.
1022 
1023   \sa isSectionHidden(), hiddenSectionCount()
1024 */
1025 
setSectionHidden(int logicalIndex,bool hide)1026 void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
1027 {
1028     Q_D(QHeaderView);
1029     if (logicalIndex < 0 || logicalIndex >= count())
1030         return;
1031 
1032     d->executePostedLayout();
1033     int visual = visualIndex(logicalIndex);
1034     Q_ASSERT(visual != -1);
1035     if (hide == d->isVisualIndexHidden(visual))
1036         return;
1037     if (hide) {
1038         const bool isHidingLastSection = (stretchLastSection() && logicalIndex == d->lastSectionLogicalIdx);
1039         if (isHidingLastSection)
1040             d->restoreSizeOnPrevLastSection(); // Restore here/now to get the right restore size.
1041         int size = d->headerSectionSize(visual);
1042         if (!d->hasAutoResizeSections())
1043             resizeSection(logicalIndex, 0);
1044         d->hiddenSectionSize.insert(logicalIndex, size);
1045         d->setVisualIndexHidden(visual, true);
1046         if (isHidingLastSection)
1047             d->setNewLastSection(d->lastVisibleVisualIndex());
1048         if (d->hasAutoResizeSections())
1049             d->doDelayedResizeSections();
1050     } else {
1051         int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
1052         d->hiddenSectionSize.remove(logicalIndex);
1053         d->setVisualIndexHidden(visual, false);
1054         resizeSection(logicalIndex, size);
1055 
1056         const bool newLastSection = (stretchLastSection() && visual > visualIndex(d->lastSectionLogicalIdx));
1057         if (newLastSection) {
1058             d->restoreSizeOnPrevLastSection();
1059             d->setNewLastSection(visual);
1060         }
1061     }
1062 }
1063 
1064 /*!
1065     Returns the number of sections in the header.
1066 
1067     \sa sectionCountChanged(), length()
1068 */
1069 
count() const1070 int QHeaderView::count() const
1071 {
1072     Q_D(const QHeaderView);
1073     //Q_ASSERT(d->sectionCount == d->headerSectionCount());
1074     // ### this may affect the lazy layout
1075     d->executePostedLayout();
1076     return d->sectionCount();
1077 }
1078 
1079 /*!
1080     Returns the visual index position of the section specified by the given
1081     \a logicalIndex, or -1 otherwise.
1082 
1083     Hidden sections still have valid visual indexes.
1084 
1085     \sa logicalIndex()
1086 */
1087 
visualIndex(int logicalIndex) const1088 int QHeaderView::visualIndex(int logicalIndex) const
1089 {
1090     Q_D(const QHeaderView);
1091     if (logicalIndex < 0)
1092         return -1;
1093     d->executePostedLayout();
1094     if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
1095         if (logicalIndex < d->sectionCount())
1096             return logicalIndex;
1097     } else if (logicalIndex < d->visualIndices.count()) {
1098         int visual = d->visualIndices.at(logicalIndex);
1099         Q_ASSERT(visual < d->sectionCount());
1100         return visual;
1101     }
1102     return -1;
1103 }
1104 
1105 /*!
1106     Returns the logicalIndex for the section at the given \a visualIndex
1107     position, or -1 if visualIndex < 0 or visualIndex >= QHeaderView::count().
1108 
1109     Note that the visualIndex is not affected by hidden sections.
1110 
1111     \sa visualIndex(), sectionPosition()
1112 */
1113 
logicalIndex(int visualIndex) const1114 int QHeaderView::logicalIndex(int visualIndex) const
1115 {
1116     Q_D(const QHeaderView);
1117     if (visualIndex < 0 || visualIndex >= d->sectionCount())
1118         return -1;
1119     return d->logicalIndex(visualIndex);
1120 }
1121 
1122 /*!
1123     \since 5.0
1124 
1125     If \a movable is true, the header sections may be moved by the user;
1126     otherwise they are fixed in place.
1127 
1128     When used in combination with QTreeView, the first column is not
1129     movable (since it contains the tree structure), by default.
1130     You can make it movable with setFirstSectionMovable(true).
1131 
1132     \sa sectionsMovable(), sectionMoved()
1133     \sa setFirstSectionMovable()
1134 */
1135 
setSectionsMovable(bool movable)1136 void QHeaderView::setSectionsMovable(bool movable)
1137 {
1138     Q_D(QHeaderView);
1139     d->movableSections = movable;
1140 }
1141 
1142 /*!
1143     \since 5.0
1144 
1145     Returns \c true if the header can be moved by the user; otherwise returns
1146     false.
1147 
1148     By default, sections are movable in QTreeView (except for the first one),
1149     and not movable in QTableView.
1150 
1151     \sa setSectionsMovable()
1152 */
1153 
sectionsMovable() const1154 bool QHeaderView::sectionsMovable() const
1155 {
1156     Q_D(const QHeaderView);
1157     return d->movableSections;
1158 }
1159 
1160 /*!
1161     \property QHeaderView::firstSectionMovable
1162     \brief Whether the first column can be moved by the user
1163 
1164     This property controls whether the first column can be moved by the user.
1165     In a QTreeView, the first column holds the tree structure and is
1166     therefore non-movable by default, even after setSectionsMovable(true).
1167 
1168     It can be made movable again, for instance in the case of flat lists
1169     without a tree structure, by calling this method.
1170     In such a scenario, it is recommended to call QTreeView::setRootIsDecorated(false)
1171     as well.
1172 
1173     Setting it to true has no effect unless setSectionsMovable(true) is called
1174     as well.
1175 
1176     \sa setSectionsMovable()
1177     \since 5.11
1178 */
setFirstSectionMovable(bool movable)1179 void QHeaderView::setFirstSectionMovable(bool movable)
1180 {
1181     Q_D(QHeaderView);
1182     d->allowUserMoveOfSection0 = movable;
1183 }
1184 
isFirstSectionMovable() const1185 bool QHeaderView::isFirstSectionMovable() const
1186 {
1187     Q_D(const QHeaderView);
1188     return d->allowUserMoveOfSection0;
1189 }
1190 
1191 /*!
1192     \since 5.0
1193 
1194     If \a clickable is true, the header will respond to single clicks.
1195 
1196     \sa sectionsClickable(), sectionClicked(), sectionPressed(),
1197     setSortIndicatorShown()
1198 */
1199 
setSectionsClickable(bool clickable)1200 void QHeaderView::setSectionsClickable(bool clickable)
1201 {
1202     Q_D(QHeaderView);
1203     d->clickableSections = clickable;
1204 }
1205 
1206 /*!
1207     \since 5.0
1208 
1209     Returns \c true if the header is clickable; otherwise returns \c false. A
1210     clickable header could be set up to allow the user to change the
1211     representation of the data in the view related to the header.
1212 
1213     \sa setSectionsClickable()
1214 */
1215 
sectionsClickable() const1216 bool QHeaderView::sectionsClickable() const
1217 {
1218     Q_D(const QHeaderView);
1219     return d->clickableSections;
1220 }
1221 
setHighlightSections(bool highlight)1222 void QHeaderView::setHighlightSections(bool highlight)
1223 {
1224     Q_D(QHeaderView);
1225     d->highlightSelected = highlight;
1226 }
1227 
highlightSections() const1228 bool QHeaderView::highlightSections() const
1229 {
1230     Q_D(const QHeaderView);
1231     return d->highlightSelected;
1232 }
1233 
1234 /*!
1235     \since 5.0
1236 
1237     Sets the constraints on how the header can be resized to those described
1238     by the given \a mode.
1239 
1240     \sa length(), sectionResized()
1241 */
1242 
setSectionResizeMode(ResizeMode mode)1243 void QHeaderView::setSectionResizeMode(ResizeMode mode)
1244 {
1245     Q_D(QHeaderView);
1246     initializeSections();
1247     d->stretchSections = (mode == Stretch ? count() : 0);
1248     d->contentsSections =  (mode == ResizeToContents ? count() : 0);
1249     d->setGlobalHeaderResizeMode(mode);
1250     if (d->hasAutoResizeSections())
1251         d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1252 }
1253 
1254 /*!
1255     \since 5.0
1256 
1257     Sets the constraints on how the section specified by \a logicalIndex in
1258     the header can be resized to those described by the given \a mode. The logical
1259     index should exist at the time this function is called.
1260 
1261     \note This setting will be ignored for the last section if the stretchLastSection
1262     property is set to true. This is the default for the horizontal headers provided
1263     by QTreeView.
1264 
1265     \sa setStretchLastSection(), resizeContentsPrecision()
1266 */
1267 
setSectionResizeMode(int logicalIndex,ResizeMode mode)1268 void QHeaderView::setSectionResizeMode(int logicalIndex, ResizeMode mode)
1269 {
1270     Q_D(QHeaderView);
1271     int visual = visualIndex(logicalIndex);
1272     Q_ASSERT(visual != -1);
1273 
1274     ResizeMode old = d->headerSectionResizeMode(visual);
1275     d->setHeaderSectionResizeMode(visual, mode);
1276 
1277     if (mode == Stretch && old != Stretch)
1278         ++d->stretchSections;
1279     else if (mode == ResizeToContents && old != ResizeToContents)
1280         ++d->contentsSections;
1281     else if (mode != Stretch && old == Stretch)
1282         --d->stretchSections;
1283     else if (mode != ResizeToContents && old == ResizeToContents)
1284         --d->contentsSections;
1285 
1286     if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
1287         d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1288 }
1289 
1290 /*!
1291     \since 5.0
1292 
1293     Returns the resize mode that applies to the section specified by the given
1294     \a logicalIndex.
1295 
1296     \sa setSectionResizeMode()
1297 */
1298 
sectionResizeMode(int logicalIndex) const1299 QHeaderView::ResizeMode QHeaderView::sectionResizeMode(int logicalIndex) const
1300 {
1301     Q_D(const QHeaderView);
1302     int visual = visualIndex(logicalIndex);
1303     if (visual == -1)
1304         return Fixed; //the default value
1305     return d->headerSectionResizeMode(visual);
1306 }
1307 
1308 /*!
1309    \since 5.2
1310    Sets how precise QHeaderView should calculate the size when ResizeToContents is used.
1311    A low value will provide a less accurate but fast auto resize while a higher
1312    value will provide a more accurate resize that however can be slow.
1313 
1314    The number \a precision specifies how many sections that should be consider
1315    when calculating the preferred size.
1316 
1317    The default value is 1000 meaning that a horizontal column with auto-resize will look
1318    at maximum 1000 rows on calculating when doing an auto resize.
1319 
1320    Special value 0 means that it will look at only the visible area.
1321    Special value -1 will imply looking at all elements.
1322 
1323    This value is used in QTableView::sizeHintForColumn(), QTableView::sizeHintForRow()
1324    and QTreeView::sizeHintForColumn(). Reimplementing these functions can make this
1325    function not having an effect.
1326 
1327     \sa resizeContentsPrecision(), setSectionResizeMode(), resizeSections(), QTableView::sizeHintForColumn(), QTableView::sizeHintForRow(), QTreeView::sizeHintForColumn()
1328 */
1329 
setResizeContentsPrecision(int precision)1330 void QHeaderView::setResizeContentsPrecision(int precision)
1331 {
1332     Q_D(QHeaderView);
1333     d->resizeContentsPrecision = precision;
1334 }
1335 
1336 /*!
1337   \since 5.2
1338   Returns how precise QHeaderView will calculate on ResizeToContents.
1339 
1340   \sa setResizeContentsPrecision(), setSectionResizeMode()
1341 
1342 */
1343 
resizeContentsPrecision() const1344 int QHeaderView::resizeContentsPrecision() const
1345 {
1346     Q_D(const QHeaderView);
1347     return d->resizeContentsPrecision;
1348 }
1349 
1350 /*!
1351     \since 4.1
1352 
1353     Returns the number of sections that are set to resize mode stretch. In
1354     views, this can be used to see if the headerview needs to resize the
1355     sections when the view's geometry changes.
1356 
1357     \sa stretchLastSection
1358 */
1359 
stretchSectionCount() const1360 int QHeaderView::stretchSectionCount() const
1361 {
1362     Q_D(const QHeaderView);
1363     return d->stretchSections;
1364 }
1365 
1366 /*!
1367   \property QHeaderView::showSortIndicator
1368   \brief whether the sort indicator is shown
1369 
1370   By default, this property is \c false.
1371 
1372   \sa setSectionsClickable()
1373 */
1374 
setSortIndicatorShown(bool show)1375 void QHeaderView::setSortIndicatorShown(bool show)
1376 {
1377     Q_D(QHeaderView);
1378     if (d->sortIndicatorShown == show)
1379         return;
1380 
1381     d->sortIndicatorShown = show;
1382 
1383     if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
1384         return;
1385 
1386     if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents)
1387         resizeSections();
1388 
1389     d->viewport->update();
1390 }
1391 
isSortIndicatorShown() const1392 bool QHeaderView::isSortIndicatorShown() const
1393 {
1394     Q_D(const QHeaderView);
1395     return d->sortIndicatorShown;
1396 }
1397 
1398 /*!
1399     Sets the sort indicator for the section specified by the given
1400     \a logicalIndex in the direction specified by \a order, and removes the
1401     sort indicator from any other section that was showing it.
1402 
1403     \a logicalIndex may be -1, in which case no sort indicator will be shown
1404     and the model will return to its natural, unsorted order. Note that not
1405     all models support this and may even crash in this case.
1406 
1407     \sa sortIndicatorSection(), sortIndicatorOrder()
1408 */
1409 
setSortIndicator(int logicalIndex,Qt::SortOrder order)1410 void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
1411 {
1412     Q_D(QHeaderView);
1413 
1414     // This is so that people can set the position of the sort indicator before the fill the model
1415     int old = d->sortIndicatorSection;
1416     if (old == logicalIndex && order == d->sortIndicatorOrder)
1417         return;
1418     d->sortIndicatorSection = logicalIndex;
1419     d->sortIndicatorOrder = order;
1420 
1421     if (logicalIndex >= d->sectionCount()) {
1422         emit sortIndicatorChanged(logicalIndex, order);
1423         return; // nothing to do
1424     }
1425 
1426     if (old != logicalIndex
1427         && ((logicalIndex >= 0 && sectionResizeMode(logicalIndex) == ResizeToContents)
1428             || old >= d->sectionCount() || (old >= 0 && sectionResizeMode(old) == ResizeToContents))) {
1429         resizeSections();
1430         d->viewport->update();
1431     } else {
1432         if (old >= 0 && old != logicalIndex)
1433             updateSection(old);
1434         if (logicalIndex >= 0)
1435             updateSection(logicalIndex);
1436     }
1437 
1438     emit sortIndicatorChanged(logicalIndex, order);
1439 }
1440 
1441 /*!
1442     Returns the logical index of the section that has a sort indicator.
1443     By default this is section 0.
1444 
1445     \sa setSortIndicator(), sortIndicatorOrder(), setSortIndicatorShown()
1446 */
1447 
sortIndicatorSection() const1448 int QHeaderView::sortIndicatorSection() const
1449 {
1450     Q_D(const QHeaderView);
1451     return d->sortIndicatorSection;
1452 }
1453 
1454 /*!
1455     Returns the order for the sort indicator. If no section has a sort
1456     indicator the return value of this function is undefined.
1457 
1458     \sa setSortIndicator(), sortIndicatorSection()
1459 */
1460 
sortIndicatorOrder() const1461 Qt::SortOrder QHeaderView::sortIndicatorOrder() const
1462 {
1463     Q_D(const QHeaderView);
1464     return d->sortIndicatorOrder;
1465 }
1466 
1467 /*!
1468     \property QHeaderView::stretchLastSection
1469     \brief whether the last visible section in the header takes up all the
1470     available space
1471 
1472     The default value is false.
1473 
1474     \note The horizontal headers provided by QTreeView are configured with this
1475     property set to true, ensuring that the view does not waste any of the
1476     space assigned to it for its header. If this value is set to true, this
1477     property will override the resize mode set on the last section in the
1478     header.
1479 
1480     \sa setSectionResizeMode()
1481 */
stretchLastSection() const1482 bool QHeaderView::stretchLastSection() const
1483 {
1484     Q_D(const QHeaderView);
1485     return d->stretchLastSection;
1486 }
1487 
setStretchLastSection(bool stretch)1488 void QHeaderView::setStretchLastSection(bool stretch)
1489 {
1490     Q_D(QHeaderView);
1491     if (d->stretchLastSection == stretch)
1492         return;
1493     d->stretchLastSection = stretch;
1494     if (d->state != QHeaderViewPrivate::NoState)
1495         return;
1496     if (stretch) {
1497         d->setNewLastSection(d->lastVisibleVisualIndex());
1498         resizeSections();
1499     } else {
1500         d->restoreSizeOnPrevLastSection();
1501     }
1502 }
1503 
1504 /*!
1505     \since 4.2
1506     \property QHeaderView::cascadingSectionResizes
1507     \brief whether interactive resizing will be cascaded to the following
1508     sections once the section being resized by the user has reached its
1509     minimum size
1510 
1511     This property only affects sections that have \l Interactive as their
1512     resize mode.
1513 
1514     The default value is false.
1515 
1516     \sa setSectionResizeMode()
1517 */
cascadingSectionResizes() const1518 bool QHeaderView::cascadingSectionResizes() const
1519 {
1520     Q_D(const QHeaderView);
1521     return d->cascadingResizing;
1522 }
1523 
setCascadingSectionResizes(bool enable)1524 void QHeaderView::setCascadingSectionResizes(bool enable)
1525 {
1526     Q_D(QHeaderView);
1527     d->cascadingResizing = enable;
1528 }
1529 
1530 /*!
1531     \property QHeaderView::defaultSectionSize
1532     \brief the default size of the header sections before resizing.
1533 
1534     This property only affects sections that have \l Interactive or \l Fixed
1535     as their resize mode.
1536 
1537     By default, the value of this property is style dependent.
1538     Thus, when the style changes, this property updates from it.
1539     Calling setDefaultSectionSize() stops the updates, calling
1540     resetDefaultSectionSize() will restore default behavior.
1541 
1542     \sa setSectionResizeMode(), minimumSectionSize
1543 */
defaultSectionSize() const1544 int QHeaderView::defaultSectionSize() const
1545 {
1546     Q_D(const QHeaderView);
1547     return d->defaultSectionSize;
1548 }
1549 
setDefaultSectionSize(int size)1550 void QHeaderView::setDefaultSectionSize(int size)
1551 {
1552     Q_D(QHeaderView);
1553     if (size < 0 || size > maxSizeSection)
1554         return;
1555     d->setDefaultSectionSize(size);
1556 }
1557 
resetDefaultSectionSize()1558 void QHeaderView::resetDefaultSectionSize()
1559 {
1560     Q_D(QHeaderView);
1561     if (d->customDefaultSectionSize) {
1562         d->updateDefaultSectionSizeFromStyle();
1563         d->customDefaultSectionSize = false;
1564     }
1565 }
1566 
1567 /*!
1568     \since 4.2
1569     \property QHeaderView::minimumSectionSize
1570     \brief the minimum size of the header sections.
1571 
1572     The minimum section size is the smallest section size allowed. If the
1573     minimum section size is set to -1, QHeaderView will use the maximum of
1574     the \l{QApplication::globalStrut()}{global strut} or the
1575     \l{fontMetrics()}{font metrics} size.
1576 
1577     This property is honored by all \l{ResizeMode}{resize modes}.
1578 
1579     \sa setSectionResizeMode(), defaultSectionSize
1580 */
minimumSectionSize() const1581 int QHeaderView::minimumSectionSize() const
1582 {
1583     Q_D(const QHeaderView);
1584     if (d->minimumSectionSize == -1) {
1585         QSize strut = QApplication::globalStrut();
1586         int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this);
1587         if (d->orientation == Qt::Horizontal)
1588             return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
1589         return qMax(strut.height(), (fontMetrics().height() + margin));
1590     }
1591     return d->minimumSectionSize;
1592 }
1593 
setMinimumSectionSize(int size)1594 void QHeaderView::setMinimumSectionSize(int size)
1595 {
1596     Q_D(QHeaderView);
1597     if (size < -1 || size > maxSizeSection)
1598         return;
1599     // larger new min size - check current section sizes
1600     const bool needSizeCheck = size > d->minimumSectionSize;
1601     d->minimumSectionSize = size;
1602     if (d->minimumSectionSize > maximumSectionSize())
1603         setMaximumSectionSize(size);
1604 
1605     if (needSizeCheck) {
1606         if (d->hasAutoResizeSections()) {
1607             d->doDelayedResizeSections();
1608         } else {
1609             for (int visual = 0; visual < d->sectionCount(); ++visual) {
1610                 if (d->isVisualIndexHidden(visual))
1611                     continue;
1612                 if (d->headerSectionSize(visual) < d->minimumSectionSize)
1613                     resizeSection(logicalIndex(visual), size);
1614             }
1615         }
1616     }
1617 
1618 }
1619 
1620 /*!
1621     \since 5.2
1622     \property QHeaderView::maximumSectionSize
1623     \brief the maximum size of the header sections.
1624 
1625     The maximum section size is the largest section size allowed.
1626     The default value for this property is 1048575, which is also the largest
1627     possible size for a section. Setting maximum to -1 will reset the value to
1628     the largest section size.
1629 
1630     With exception of stretch this property is honored by all \l{ResizeMode}{resize modes}
1631 
1632     \sa setSectionResizeMode(), defaultSectionSize
1633 */
maximumSectionSize() const1634 int QHeaderView::maximumSectionSize() const
1635 {
1636     Q_D(const QHeaderView);
1637     if (d->maximumSectionSize == -1)
1638         return maxSizeSection;
1639     return d->maximumSectionSize;
1640 }
1641 
setMaximumSectionSize(int size)1642 void QHeaderView::setMaximumSectionSize(int size)
1643 {
1644     Q_D(QHeaderView);
1645     if (size == -1) {
1646         d->maximumSectionSize = maxSizeSection;
1647         return;
1648     }
1649     if (size < 0 || size > maxSizeSection)
1650         return;
1651     if (minimumSectionSize() > size)
1652         d->minimumSectionSize = size;
1653 
1654     // smaller new max size - check current section sizes
1655     const bool needSizeCheck = size < d->maximumSectionSize;
1656     d->maximumSectionSize = size;
1657 
1658     if (needSizeCheck) {
1659         if (d->hasAutoResizeSections()) {
1660             d->doDelayedResizeSections();
1661         } else {
1662             for (int visual = 0; visual < d->sectionCount(); ++visual) {
1663                 if (d->isVisualIndexHidden(visual))
1664                     continue;
1665                 if (d->headerSectionSize(visual) > d->maximumSectionSize)
1666                     resizeSection(logicalIndex(visual), size);
1667             }
1668         }
1669     }
1670 }
1671 
1672 
1673 /*!
1674     \since 4.1
1675     \property QHeaderView::defaultAlignment
1676     \brief the default alignment of the text in each header section
1677 */
1678 
defaultAlignment() const1679 Qt::Alignment QHeaderView::defaultAlignment() const
1680 {
1681     Q_D(const QHeaderView);
1682     return d->defaultAlignment;
1683 }
1684 
setDefaultAlignment(Qt::Alignment alignment)1685 void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
1686 {
1687     Q_D(QHeaderView);
1688     if (d->defaultAlignment == alignment)
1689         return;
1690 
1691     d->defaultAlignment = alignment;
1692     d->viewport->update();
1693 }
1694 
1695 /*!
1696     \internal
1697 */
doItemsLayout()1698 void QHeaderView::doItemsLayout()
1699 {
1700     initializeSections();
1701     QAbstractItemView::doItemsLayout();
1702 }
1703 
1704 /*!
1705     Returns \c true if sections in the header has been moved; otherwise returns
1706     false;
1707 
1708     \sa moveSection()
1709 */
sectionsMoved() const1710 bool QHeaderView::sectionsMoved() const
1711 {
1712     Q_D(const QHeaderView);
1713     return !d->visualIndices.isEmpty();
1714 }
1715 
1716 /*!
1717     \since 4.1
1718 
1719     Returns \c true if sections in the header has been hidden; otherwise returns
1720     false;
1721 
1722     \sa setSectionHidden()
1723 */
sectionsHidden() const1724 bool QHeaderView::sectionsHidden() const
1725 {
1726     Q_D(const QHeaderView);
1727     return !d->hiddenSectionSize.isEmpty();
1728 }
1729 
1730 #ifndef QT_NO_DATASTREAM
1731 /*!
1732     \since 4.3
1733 
1734     Saves the current state of this header view.
1735 
1736     To restore the saved state, pass the return value to restoreState().
1737 
1738     \sa restoreState()
1739 */
saveState() const1740 QByteArray QHeaderView::saveState() const
1741 {
1742     Q_D(const QHeaderView);
1743     QByteArray data;
1744     QDataStream stream(&data, QIODevice::WriteOnly);
1745     stream << QHeaderViewPrivate::VersionMarker;
1746     stream << 0; // current version is 0
1747     d->write(stream);
1748     return data;
1749 }
1750 
1751 /*!
1752     \since 4.3
1753     Restores the \a state of this header view.
1754     This function returns \c true if the state was restored; otherwise returns
1755     false.
1756 
1757     \sa saveState()
1758 */
restoreState(const QByteArray & state)1759 bool QHeaderView::restoreState(const QByteArray &state)
1760 {
1761     Q_D(QHeaderView);
1762     if (state.isEmpty())
1763         return false;
1764     QByteArray data = state;
1765     QDataStream stream(&data, QIODevice::ReadOnly);
1766     int marker;
1767     int ver;
1768     stream >> marker;
1769     stream >> ver;
1770     if (stream.status() != QDataStream::Ok
1771         || marker != QHeaderViewPrivate::VersionMarker
1772         || ver != 0) // current version is 0
1773         return false;
1774 
1775     if (d->read(stream)) {
1776         emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
1777         d->viewport->update();
1778         return true;
1779     }
1780     return false;
1781 }
1782 #endif // QT_NO_DATASTREAM
1783 
1784 /*!
1785   \reimp
1786 */
reset()1787 void QHeaderView::reset()
1788 {
1789     Q_D(QHeaderView);
1790     QAbstractItemView::reset();
1791     // it would be correct to call clear, but some apps rely
1792     // on the header keeping the sections, even after calling reset
1793     //d->clear();
1794     initializeSections();
1795     d->invalidateCachedSizeHint();
1796 }
1797 
1798 /*!
1799     Updates the changed header sections with the given \a orientation, from
1800     \a logicalFirst to \a logicalLast inclusive.
1801 */
headerDataChanged(Qt::Orientation orientation,int logicalFirst,int logicalLast)1802 void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
1803 {
1804     Q_D(QHeaderView);
1805     if (d->orientation != orientation)
1806         return;
1807 
1808     if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
1809         return;
1810 
1811     d->invalidateCachedSizeHint();
1812 
1813     int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
1814 
1815     for (int section = logicalFirst; section <= logicalLast; ++section) {
1816         const int visual = visualIndex(section);
1817         firstVisualIndex = qMin(firstVisualIndex, visual);
1818         lastVisualIndex =  qMax(lastVisualIndex,  visual);
1819     }
1820 
1821     d->executePostedResize();
1822     const int first = d->headerSectionPosition(firstVisualIndex),
1823               last = d->headerSectionPosition(lastVisualIndex)
1824                         + d->headerSectionSize(lastVisualIndex);
1825 
1826     if (orientation == Qt::Horizontal) {
1827         d->viewport->update(first, 0, last - first, d->viewport->height());
1828     } else {
1829         d->viewport->update(0, first, d->viewport->width(), last - first);
1830     }
1831 }
1832 
1833 /*!
1834     \internal
1835     \since 4.2
1836 
1837     Updates the section specified by the given \a logicalIndex.
1838 */
1839 
updateSection(int logicalIndex)1840 void QHeaderView::updateSection(int logicalIndex)
1841 {
1842     Q_D(QHeaderView);
1843     if (d->orientation == Qt::Horizontal)
1844         d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
1845                                   0, sectionSize(logicalIndex), d->viewport->height()));
1846     else
1847         d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
1848                                   d->viewport->width(), sectionSize(logicalIndex)));
1849 }
1850 
1851 /*!
1852     Resizes the sections according to their size hints. Normally, you do not
1853     have to call this function.
1854 */
1855 
resizeSections()1856 void QHeaderView::resizeSections()
1857 {
1858     Q_D(QHeaderView);
1859     if (d->hasAutoResizeSections())
1860         d->resizeSections(Interactive, false); // no global resize mode
1861 }
1862 
1863 /*!
1864     This slot is called when sections are inserted into the \a parent.
1865     \a logicalFirst and \a logicalLast indices signify where the new sections
1866     were inserted.
1867 
1868     If only one section is inserted, \a logicalFirst and \a logicalLast will
1869     be the same.
1870 */
1871 
sectionsInserted(const QModelIndex & parent,int logicalFirst,int logicalLast)1872 void QHeaderView::sectionsInserted(const QModelIndex &parent,
1873                                    int logicalFirst, int logicalLast)
1874 {
1875     Q_D(QHeaderView);
1876     if (parent != d->root)
1877         return; // we only handle changes in the root level
1878     int oldCount = d->sectionCount();
1879 
1880     d->invalidateCachedSizeHint();
1881 
1882     if (d->state == QHeaderViewPrivate::ResizeSection)
1883         d->preventCursorChangeInSetOffset = true;
1884 
1885     // add the new sections
1886     int insertAt = logicalFirst;
1887     int insertCount = logicalLast - logicalFirst + 1;
1888 
1889     bool lastSectionActualChange = false;
1890     if (stretchLastSection()) {
1891 
1892         int visualIndexForStretch = d->lastSectionLogicalIdx;
1893         if (d->lastSectionLogicalIdx >= 0 && d->lastSectionLogicalIdx < d->visualIndices.size())
1894             visualIndexForStretch = d->visualIndices[d->lastSectionLogicalIdx]; // We cannot call visualIndex since it executes executePostedLayout()
1895                                                                                 // and it is likely to bypass initializeSections() and we may end up here again. Doing the insert twice.
1896 
1897         if (d->lastSectionLogicalIdx < 0 || insertAt >= visualIndexForStretch)
1898             lastSectionActualChange = true;
1899 
1900         if (d->lastSectionLogicalIdx >= logicalFirst)
1901             d->lastSectionLogicalIdx += insertCount; // We do not want to emit resize before we have fixed the count
1902     }
1903 
1904     QHeaderViewPrivate::SectionItem section(d->defaultSectionSize, d->globalResizeMode);
1905     d->sectionStartposRecalc = true;
1906 
1907     if (d->sectionItems.isEmpty() || insertAt >= d->sectionItems.count()) {
1908         int insertLength = d->defaultSectionSize * insertCount;
1909         d->length += insertLength;
1910         d->sectionItems.insert(d->sectionItems.count(), insertCount, section); // append
1911     } else {
1912         // separate them out into their own sections
1913         int insertLength = d->defaultSectionSize * insertCount;
1914         d->length += insertLength;
1915         d->sectionItems.insert(insertAt, insertCount, section);
1916     }
1917 
1918     // update sorting column
1919     if (d->sortIndicatorSection >= logicalFirst)
1920         d->sortIndicatorSection += insertCount;
1921 
1922     // update resize mode section counts
1923     if (d->globalResizeMode == Stretch)
1924         d->stretchSections = d->sectionCount();
1925     else if (d->globalResizeMode == ResizeToContents)
1926         d->contentsSections = d->sectionCount();
1927 
1928     // clear selection cache
1929     d->sectionSelected.clear();
1930 
1931     // update mapping
1932     if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
1933         Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
1934         int mappingCount = d->visualIndices.count();
1935         for (int i = 0; i < mappingCount; ++i) {
1936             if (d->visualIndices.at(i) >= logicalFirst)
1937                d->visualIndices[i] += insertCount;
1938             if (d->logicalIndices.at(i) >= logicalFirst)
1939                 d->logicalIndices[i] += insertCount;
1940         }
1941         for (int j = logicalFirst; j <= logicalLast; ++j) {
1942             d->visualIndices.insert(j, j);
1943             d->logicalIndices.insert(j, j);
1944         }
1945     }
1946 
1947     // insert sections into hiddenSectionSize
1948     QHash<int, int> newHiddenSectionSize; // from logical index to section size
1949     for (QHash<int, int>::const_iterator it = d->hiddenSectionSize.cbegin(),
1950          end = d->hiddenSectionSize.cend(); it != end; ++it) {
1951         const int oldIndex = it.key();
1952         const int newIndex = (oldIndex < logicalFirst) ? oldIndex : oldIndex + insertCount;
1953         newHiddenSectionSize[newIndex] = it.value();
1954     }
1955     d->hiddenSectionSize.swap(newHiddenSectionSize);
1956 
1957     d->doDelayedResizeSections();
1958     emit sectionCountChanged(oldCount, count());
1959 
1960     if (lastSectionActualChange)
1961         d->maybeRestorePrevLastSectionAndStretchLast();
1962 
1963     // if the new sections were not updated by resizing, we need to update now
1964     if (!d->hasAutoResizeSections())
1965         d->viewport->update();
1966 }
1967 
1968 /*!
1969     This slot is called when sections are removed from the \a parent.
1970     \a logicalFirst and \a logicalLast signify where the sections were removed.
1971 
1972     If only one section is removed, \a logicalFirst and \a logicalLast will
1973     be the same.
1974 */
1975 
sectionsAboutToBeRemoved(const QModelIndex & parent,int logicalFirst,int logicalLast)1976 void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
1977                                            int logicalFirst, int logicalLast)
1978 {
1979     Q_UNUSED(parent);
1980     Q_UNUSED(logicalFirst);
1981     Q_UNUSED(logicalLast);
1982 }
1983 
updateHiddenSections(int logicalFirst,int logicalLast)1984 void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
1985 {
1986     Q_Q(QHeaderView);
1987     const int changeCount = logicalLast - logicalFirst + 1;
1988 
1989     // remove sections from hiddenSectionSize
1990     QHash<int, int> newHiddenSectionSize; // from logical index to section size
1991     for (int i = 0; i < logicalFirst; ++i)
1992         if (q->isSectionHidden(i))
1993             newHiddenSectionSize[i] = hiddenSectionSize[i];
1994     for (int j = logicalLast + 1; j < sectionCount(); ++j)
1995         if (q->isSectionHidden(j))
1996             newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
1997     hiddenSectionSize = newHiddenSectionSize;
1998 }
1999 
_q_sectionsRemoved(const QModelIndex & parent,int logicalFirst,int logicalLast)2000 void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
2001                                             int logicalFirst, int logicalLast)
2002 {
2003     Q_Q(QHeaderView);
2004     if (parent != root)
2005         return; // we only handle changes in the root level
2006     if (qMin(logicalFirst, logicalLast) < 0
2007         || qMax(logicalLast, logicalFirst) >= sectionCount())
2008         return;
2009     int oldCount = q->count();
2010     int changeCount = logicalLast - logicalFirst + 1;
2011 
2012     if (state == QHeaderViewPrivate::ResizeSection)
2013         preventCursorChangeInSetOffset = true;
2014 
2015     updateHiddenSections(logicalFirst, logicalLast);
2016 
2017     if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
2018         //Q_ASSERT(headerSectionCount() == sectionCount);
2019         removeSectionsFromSectionItems(logicalFirst, logicalLast);
2020     } else {
2021         if (logicalFirst == logicalLast) { // Remove just one index.
2022             int l = logicalFirst;
2023             int visual = visualIndices.at(l);
2024             Q_ASSERT(sectionCount() == logicalIndices.count());
2025             for (int v = 0; v < sectionCount(); ++v) {
2026                 if (v > visual) {
2027                     int logical = logicalIndices.at(v);
2028                     --(visualIndices[logical]);
2029                 }
2030                 if (logicalIndex(v) > l) // no need to move the positions before l
2031                     --(logicalIndices[v]);
2032             }
2033             logicalIndices.remove(visual);
2034             visualIndices.remove(l);
2035             //Q_ASSERT(headerSectionCount() == sectionCount);
2036             removeSectionsFromSectionItems(visual, visual);
2037         } else {
2038             sectionStartposRecalc = true; // We will need to recalc positions after removing items
2039             for (int u = 0; u < sectionItems.count(); ++u)  // Store section info
2040                 sectionItems.at(u).tmpLogIdx = logicalIndices.at(u);
2041             for (int v = sectionItems.count() - 1; v >= 0; --v) {  // Remove the sections
2042                 if (logicalFirst <= sectionItems.at(v).tmpLogIdx && sectionItems.at(v).tmpLogIdx <= logicalLast)
2043                     removeSectionsFromSectionItems(v, v);
2044             }
2045             visualIndices.resize(sectionItems.count());
2046             logicalIndices.resize(sectionItems.count());
2047             int* visual_data = visualIndices.data();
2048             int* logical_data = logicalIndices.data();
2049             for (int w = 0; w < sectionItems.count(); ++w) { // Restore visual and logical indexes
2050                 int logindex = sectionItems.at(w).tmpLogIdx;
2051                 if (logindex > logicalFirst)
2052                     logindex -= changeCount;
2053                 visual_data[logindex] = w;
2054                 logical_data[w] = logindex;
2055             }
2056         }
2057         // ### handle sectionSelection (sectionHidden is handled by updateHiddenSections)
2058     }
2059 
2060     // update sorting column
2061     if (sortIndicatorSection >= logicalFirst) {
2062         if (sortIndicatorSection <= logicalLast)
2063             sortIndicatorSection = -1;
2064         else
2065             sortIndicatorSection -= changeCount;
2066     }
2067 
2068     // if we only have the last section (the "end" position) left, the header is empty
2069     if (sectionCount() <= 0)
2070         clear();
2071     invalidateCachedSizeHint();
2072     emit q->sectionCountChanged(oldCount, q->count());
2073 
2074     if (q->stretchLastSection()) {
2075         const bool lastSectionRemoved = lastSectionLogicalIdx >= logicalFirst && lastSectionLogicalIdx <= logicalLast;
2076         if (lastSectionRemoved)
2077             setNewLastSection(lastVisibleVisualIndex());
2078         else
2079             lastSectionLogicalIdx = logicalIndex(lastVisibleVisualIndex()); // Just update the last log index.
2080         doDelayedResizeSections();
2081     }
2082 
2083     viewport->update();
2084 }
2085 
_q_sectionsAboutToBeMoved(const QModelIndex & sourceParent,int logicalStart,int logicalEnd,const QModelIndex & destinationParent,int logicalDestination)2086 void QHeaderViewPrivate::_q_sectionsAboutToBeMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2087 {
2088     if (sourceParent != root || destinationParent != root)
2089         return; // we only handle changes in the root level
2090     Q_UNUSED(logicalStart);
2091     Q_UNUSED(logicalEnd);
2092     Q_UNUSED(logicalDestination);
2093     _q_sectionsAboutToBeChanged();
2094 }
2095 
_q_sectionsMoved(const QModelIndex & sourceParent,int logicalStart,int logicalEnd,const QModelIndex & destinationParent,int logicalDestination)2096 void QHeaderViewPrivate::_q_sectionsMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2097 {
2098     if (sourceParent != root || destinationParent != root)
2099         return; // we only handle changes in the root level
2100     Q_UNUSED(logicalStart);
2101     Q_UNUSED(logicalEnd);
2102     Q_UNUSED(logicalDestination);
2103     _q_sectionsChanged();
2104 }
2105 
_q_sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &,QAbstractItemModel::LayoutChangeHint hint)2106 void QHeaderViewPrivate::_q_sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &,
2107                                                      QAbstractItemModel::LayoutChangeHint hint)
2108 {
2109     if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2110         (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2111         return;
2112 
2113     //if there is no row/column we can't have mapping for columns
2114     //because no QModelIndex in the model would be valid
2115     // ### this is far from being bullet-proof and we would need a real system to
2116     // ### map columns or rows persistently
2117     if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
2118         || model->columnCount(root) == 0)
2119         return;
2120 
2121     layoutChangePersistentSections.clear();
2122     layoutChangePersistentSections.reserve(std::min(10, sectionItems.count()));
2123     // after layoutChanged another section can be last stretched section
2124     if (stretchLastSection && lastSectionLogicalIdx >= 0 && lastSectionLogicalIdx < sectionItems.count()) {
2125         const int visual = visualIndex(lastSectionLogicalIdx);
2126         if (visual >= 0 && visual < sectionItems.size()) {
2127             auto &itemRef = sectionItems[visual];
2128             if (itemRef.size != lastSectionSize) {
2129                 length += lastSectionSize - itemRef.size;
2130                 itemRef.size = lastSectionSize;
2131             }
2132         }
2133     }
2134     for (int i = 0; i < sectionItems.size(); ++i) {
2135         auto s = sectionItems.at(i);
2136         // only add if the section is not default and not visually moved
2137         if (s.size == defaultSectionSize && !s.isHidden && s.resizeMode == globalResizeMode)
2138             continue;
2139 
2140         const int logical = logicalIndex(i);
2141         if (s.isHidden)
2142             s.size = hiddenSectionSize.value(logical);
2143 
2144         // ### note that we are using column or row 0
2145         layoutChangePersistentSections.append({orientation == Qt::Horizontal
2146                                                   ? model->index(0, logical, root)
2147                                                   : model->index(logical, 0, root),
2148                                               s});
2149     }
2150 }
2151 
_q_sectionsChanged(const QList<QPersistentModelIndex> &,QAbstractItemModel::LayoutChangeHint hint)2152 void QHeaderViewPrivate::_q_sectionsChanged(const QList<QPersistentModelIndex> &,
2153                                             QAbstractItemModel::LayoutChangeHint hint)
2154 {
2155     if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2156         (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2157         return;
2158 
2159     Q_Q(QHeaderView);
2160     viewport->update();
2161 
2162     const auto oldPersistentSections = layoutChangePersistentSections;
2163     layoutChangePersistentSections.clear();
2164 
2165     const int newCount = modelSectionCount();
2166     const int oldCount = sectionItems.size();
2167     if (newCount == 0) {
2168         clear();
2169         if (oldCount != 0)
2170             emit q->sectionCountChanged(oldCount, 0);
2171         return;
2172     }
2173 
2174     bool hasPersistantIndexes = false;
2175     for (const auto &item : oldPersistentSections) {
2176         if (item.index.isValid()) {
2177             hasPersistantIndexes = true;
2178             break;
2179         }
2180     }
2181 
2182     // Though far from perfect we here try to retain earlier/existing behavior
2183     // ### See QHeaderViewPrivate::_q_layoutAboutToBeChanged()
2184     // When we don't have valid hasPersistantIndexes it can be due to
2185     // - all sections are default sections
2186     // - the row/column 0 which is used for persistent indexes is gone
2187     // - all non-default sections were removed
2188     // case one is trivial, in case two we assume nothing else changed (it's the best
2189     // guess we can do - everything else can not be handled correctly for now)
2190     // case three can not be handled correctly with layoutChanged - removeSections
2191     // should be used instead for this
2192     if (!hasPersistantIndexes) {
2193         if (oldCount != newCount)
2194             q->initializeSections();
2195         return;
2196     }
2197 
2198     // adjust section size
2199     if (newCount != oldCount) {
2200         const int min = qBound(0, oldCount, newCount - 1);
2201         q->initializeSections(min, newCount - 1);
2202     }
2203     // reset sections
2204     sectionItems.fill(SectionItem(defaultSectionSize, globalResizeMode), newCount);
2205 
2206     // all hidden sections are in oldPersistentSections
2207     hiddenSectionSize.clear();
2208 
2209     for (const auto &item : oldPersistentSections) {
2210         const auto &index = item.index;
2211         if (!index.isValid())
2212             continue;
2213 
2214         const int newLogicalIndex = (orientation == Qt::Horizontal
2215                                      ? index.column()
2216                                      : index.row());
2217         // the new visualIndices are already adjusted / reset by initializeSections()
2218         const int newVisualIndex = visualIndex(newLogicalIndex);
2219         if (newVisualIndex < sectionItems.count()) {
2220             auto &newSection = sectionItems[newVisualIndex];
2221             newSection = item.section;
2222 
2223             if (newSection.isHidden) {
2224                 // otherwise setSectionHidden will return without doing anything
2225                 newSection.isHidden = false;
2226                 q->setSectionHidden(newLogicalIndex, true);
2227             }
2228         }
2229     }
2230 
2231     recalcSectionStartPos();
2232     length = headerLength();
2233 
2234     if (stretchLastSection) {
2235         // force rebuild of stretched section later on
2236         lastSectionLogicalIdx = -1;
2237         maybeRestorePrevLastSectionAndStretchLast();
2238     }
2239 }
2240 
2241 /*!
2242   \internal
2243 */
2244 
initializeSections()2245 void QHeaderView::initializeSections()
2246 {
2247     Q_D(QHeaderView);
2248     const int oldCount = d->sectionCount();
2249     const int newCount = d->modelSectionCount();
2250     if (newCount <= 0) {
2251         d->clear();
2252         emit sectionCountChanged(oldCount, 0);
2253     } else if (newCount != oldCount) {
2254         const int min = qBound(0, oldCount, newCount - 1);
2255         initializeSections(min, newCount - 1);
2256         if (stretchLastSection())   // we've already gotten the size hint
2257             d->maybeRestorePrevLastSectionAndStretchLast();
2258 
2259         // make sure we update the hidden sections
2260         // simulate remove from newCount to oldCount
2261         if (newCount < oldCount)
2262             d->updateHiddenSections(newCount, oldCount);
2263     }
2264 }
2265 
2266 /*!
2267     \internal
2268 */
2269 
initializeSections(int start,int end)2270 void QHeaderView::initializeSections(int start, int end)
2271 {
2272     Q_D(QHeaderView);
2273 
2274     Q_ASSERT(start >= 0);
2275     Q_ASSERT(end >= 0);
2276 
2277     d->invalidateCachedSizeHint();
2278     int oldCount = d->sectionCount();
2279 
2280     if (end + 1 < d->sectionCount()) {
2281         int newCount = end + 1;
2282         d->removeSectionsFromSectionItems(newCount, d->sectionCount() - 1);
2283         if (!d->hiddenSectionSize.isEmpty()) {
2284             if (oldCount - newCount > d->hiddenSectionSize.count()) {
2285                 for (int i = end + 1; i < d->sectionCount(); ++i)
2286                     d->hiddenSectionSize.remove(i);
2287             } else {
2288                 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
2289                 while (it != d->hiddenSectionSize.end()) {
2290                     if (it.key() > end)
2291                         it = d->hiddenSectionSize.erase(it);
2292                     else
2293                         ++it;
2294                 }
2295             }
2296         }
2297     }
2298 
2299     int newSectionCount = end + 1;
2300 
2301     if (!d->logicalIndices.isEmpty()) {
2302         if (oldCount <= newSectionCount) {
2303             d->logicalIndices.resize(newSectionCount);
2304             d->visualIndices.resize(newSectionCount);
2305             for (int i = oldCount; i < newSectionCount; ++i) {
2306                 d->logicalIndices[i] = i;
2307                 d->visualIndices[i] = i;
2308             }
2309         } else {
2310             int j = 0;
2311             for (int i = 0; i < oldCount; ++i) {
2312                 int v = d->logicalIndices.at(i);
2313                 if (v < newSectionCount) {
2314                     d->logicalIndices[j] = v;
2315                     d->visualIndices[v] = j;
2316                     j++;
2317                 }
2318             }
2319             d->logicalIndices.resize(newSectionCount);
2320             d->visualIndices.resize(newSectionCount);
2321         }
2322     }
2323 
2324     if (d->globalResizeMode == Stretch)
2325         d->stretchSections = newSectionCount;
2326     else if (d->globalResizeMode == ResizeToContents)
2327          d->contentsSections = newSectionCount;
2328 
2329     if (newSectionCount > oldCount)
2330         d->createSectionItems(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
2331     //Q_ASSERT(d->headerLength() == d->length);
2332 
2333     if (d->sectionCount() != oldCount)
2334         emit sectionCountChanged(oldCount,  d->sectionCount());
2335     d->viewport->update();
2336 }
2337 
2338 /*!
2339   \reimp
2340 */
2341 
currentChanged(const QModelIndex & current,const QModelIndex & old)2342 void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
2343 {
2344     Q_D(QHeaderView);
2345 
2346     if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
2347         if (old.isValid() && old.parent() == d->root)
2348             d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
2349                                     sectionSize(old.column()), d->viewport->height()));
2350         if (current.isValid() && current.parent() == d->root)
2351             d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
2352                                     sectionSize(current.column()), d->viewport->height()));
2353     } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
2354         if (old.isValid() && old.parent() == d->root)
2355             d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
2356                                     d->viewport->width(), sectionSize(old.row())));
2357         if (current.isValid() && current.parent() == d->root)
2358             d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2359                                     d->viewport->width(), sectionSize(current.row())));
2360     }
2361 }
2362 
2363 
2364 /*!
2365   \reimp
2366 */
2367 
event(QEvent * e)2368 bool QHeaderView::event(QEvent *e)
2369 {
2370     Q_D(QHeaderView);
2371     switch (e->type()) {
2372     case QEvent::HoverEnter: {
2373         QHoverEvent *he = static_cast<QHoverEvent*>(e);
2374         d->hover = logicalIndexAt(he->pos());
2375         if (d->hover != -1)
2376             updateSection(d->hover);
2377         break; }
2378     case QEvent::Leave:
2379     case QEvent::HoverLeave: {
2380         if (d->hover != -1)
2381             updateSection(d->hover);
2382         d->hover = -1;
2383         break; }
2384     case QEvent::HoverMove: {
2385         QHoverEvent *he = static_cast<QHoverEvent*>(e);
2386         int oldHover = d->hover;
2387         d->hover = logicalIndexAt(he->pos());
2388         if (d->hover != oldHover) {
2389             if (oldHover != -1)
2390                 updateSection(oldHover);
2391             if (d->hover != -1)
2392                 updateSection(d->hover);
2393         }
2394         break; }
2395     case QEvent::Timer: {
2396         QTimerEvent *te = static_cast<QTimerEvent*>(e);
2397         if (te->timerId() == d->delayedResize.timerId()) {
2398             d->delayedResize.stop();
2399             resizeSections();
2400         }
2401         break; }
2402     case QEvent::StyleChange:
2403         if (!d->customDefaultSectionSize)
2404             d->updateDefaultSectionSizeFromStyle();
2405         break;
2406     default:
2407         break;
2408     }
2409     return QAbstractItemView::event(e);
2410 }
2411 
2412 /*!
2413   \reimp
2414 */
2415 
paintEvent(QPaintEvent * e)2416 void QHeaderView::paintEvent(QPaintEvent *e)
2417 {
2418     Q_D(QHeaderView);
2419 
2420     if (count() == 0)
2421         return;
2422 
2423     QPainter painter(d->viewport);
2424     const QPoint offset = d->scrollDelayOffset;
2425     QRect translatedEventRect = e->rect();
2426     translatedEventRect.translate(offset);
2427 
2428     int start = -1;
2429     int end = -1;
2430     if (d->orientation == Qt::Horizontal) {
2431         start = visualIndexAt(translatedEventRect.left());
2432         end = visualIndexAt(translatedEventRect.right());
2433     } else {
2434         start = visualIndexAt(translatedEventRect.top());
2435         end = visualIndexAt(translatedEventRect.bottom());
2436     }
2437 
2438     if (d->reverse()) {
2439         start = (start == -1 ? count() - 1 : start);
2440         end = (end == -1 ? 0 : end);
2441     } else {
2442         start = (start == -1 ? 0 : start);
2443         end = (end == -1 ? count() - 1 : end);
2444     }
2445 
2446     int tmp = start;
2447     start = qMin(start, end);
2448     end = qMax(tmp, end);
2449 
2450     d->prepareSectionSelected(); // clear and resize the bit array
2451 
2452     QRect currentSectionRect;
2453     const int width = d->viewport->width();
2454     const int height = d->viewport->height();
2455     const int rtlHorizontalOffset = d->reverse() ? 1 : 0;
2456     for (int i = start; i <= end; ++i) {
2457         if (d->isVisualIndexHidden(i))
2458             continue;
2459         painter.save();
2460         const int logical = logicalIndex(i);
2461         if (d->orientation == Qt::Horizontal) {
2462             currentSectionRect.setRect(sectionViewportPosition(logical) + rtlHorizontalOffset,
2463                                        0, sectionSize(logical), height);
2464         } else {
2465             currentSectionRect.setRect(0, sectionViewportPosition(logical),
2466                                        width, sectionSize(logical));
2467         }
2468         currentSectionRect.translate(offset);
2469 
2470         QVariant variant = d->model->headerData(logical, d->orientation,
2471                                                 Qt::FontRole);
2472         if (variant.isValid() && variant.canConvert<QFont>()) {
2473             QFont sectionFont = qvariant_cast<QFont>(variant);
2474             painter.setFont(sectionFont);
2475         }
2476         paintSection(&painter, currentSectionRect, logical);
2477         painter.restore();
2478     }
2479 
2480     QStyleOption opt;
2481     opt.init(this);
2482     // Paint the area beyond where there are indexes
2483     if (d->reverse()) {
2484         opt.state |= QStyle::State_Horizontal;
2485         if (currentSectionRect.left() > translatedEventRect.left()) {
2486             opt.rect = QRect(translatedEventRect.left(), 0,
2487                              currentSectionRect.left() - translatedEventRect.left(), height);
2488             style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2489         }
2490     } else if (currentSectionRect.right() < translatedEventRect.right()) {
2491         // paint to the right
2492         opt.state |= QStyle::State_Horizontal;
2493         opt.rect = QRect(currentSectionRect.right() + 1, 0,
2494                          translatedEventRect.right() - currentSectionRect.right(), height);
2495         style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2496     } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2497         // paint the bottom section
2498         opt.state &= ~QStyle::State_Horizontal;
2499         opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2500                          width, height - currentSectionRect.bottom() - 1);
2501         style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2502     }
2503 
2504 #if 0
2505     // ### visualize sections
2506     for (int a = 0, i = 0; i < d->sectionItems.count(); ++i) {
2507         QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2508         if (d->orientation == Qt::Horizontal)
2509             painter.fillRect(a - d->offset, 0, d->sectionItems.at(i).size, 4, color);
2510         else
2511             painter.fillRect(0, a - d->offset, 4, d->sectionItems.at(i).size, color);
2512         a += d->sectionItems.at(i).size;
2513     }
2514 
2515 #endif
2516 }
2517 
2518 /*!
2519   \reimp
2520 */
2521 
mousePressEvent(QMouseEvent * e)2522 void QHeaderView::mousePressEvent(QMouseEvent *e)
2523 {
2524     Q_D(QHeaderView);
2525     if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2526         return;
2527     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2528     int handle = d->sectionHandleAt(pos);
2529     d->originalSize = -1; // clear the stored original size
2530     if (handle == -1) {
2531         d->firstPressed = d->pressed = logicalIndexAt(pos);
2532         if (d->clickableSections)
2533             emit sectionPressed(d->pressed);
2534 
2535         bool acceptMoveSection = d->movableSections;
2536         if (acceptMoveSection && d->pressed == 0 && !d->allowUserMoveOfSection0)
2537             acceptMoveSection = false; // Do not allow moving the tree nod
2538 
2539         if (acceptMoveSection) {
2540             d->section = d->target = d->pressed;
2541             if (d->section == -1)
2542                 return;
2543             d->state = QHeaderViewPrivate::MoveSection;
2544             d->setupSectionIndicator(d->section, pos);
2545         } else if (d->clickableSections && d->pressed != -1) {
2546             updateSection(d->pressed);
2547             d->state = QHeaderViewPrivate::SelectSections;
2548         }
2549     } else if (sectionResizeMode(handle) == Interactive) {
2550         d->originalSize = sectionSize(handle);
2551         d->state = QHeaderViewPrivate::ResizeSection;
2552         d->section = handle;
2553         d->preventCursorChangeInSetOffset = false;
2554     }
2555 
2556     d->firstPos = pos;
2557     d->lastPos = pos;
2558 
2559     d->clearCascadingSections();
2560 }
2561 
2562 /*!
2563   \reimp
2564 */
2565 
mouseMoveEvent(QMouseEvent * e)2566 void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2567 {
2568     Q_D(QHeaderView);
2569     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2570     if (pos < 0 && d->state != QHeaderViewPrivate::SelectSections)
2571         return;
2572     if (e->buttons() == Qt::NoButton) {
2573         // Under Cocoa, when the mouse button is released, may include an extra
2574         // simulated mouse moved event. The state of the buttons when this event
2575         // is generated is already "no button" and the code below gets executed
2576         // just before the mouseReleaseEvent and resets the state. This prevents
2577         // column dragging from working. So this code is disabled under Cocoa.
2578         d->state = QHeaderViewPrivate::NoState;
2579         d->firstPressed = d->pressed = -1;
2580     }
2581     switch (d->state) {
2582         case QHeaderViewPrivate::ResizeSection: {
2583             Q_ASSERT(d->originalSize != -1);
2584             if (d->cascadingResizing) {
2585                 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2586                 int visual = visualIndex(d->section);
2587                 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2588             } else {
2589                 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2590                 int newsize = qBound(minimumSectionSize(), d->originalSize + delta, maximumSectionSize());
2591                 resizeSection(d->section, newsize);
2592             }
2593             d->lastPos = pos;
2594             return;
2595         }
2596         case QHeaderViewPrivate::MoveSection: {
2597             if (d->shouldAutoScroll(e->pos()))
2598                 d->startAutoScroll();
2599             if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2600 #if QT_CONFIG(label)
2601                 || !d->sectionIndicator->isHidden()
2602 #endif
2603                 ) {
2604                 int visual = visualIndexAt(pos);
2605                 if (visual == -1)
2606                     return;
2607                 if (visual == 0 && logicalIndex(0) == 0 && !d->allowUserMoveOfSection0)
2608                     return;
2609 
2610                 int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2;
2611                 int moving = visualIndex(d->section);
2612                 if (visual < moving) {
2613                     if (pos < posThreshold)
2614                         d->target = d->logicalIndex(visual);
2615                     else
2616                         d->target = d->logicalIndex(visual + 1);
2617                 } else if (visual > moving) {
2618                     if (pos > posThreshold)
2619                         d->target = d->logicalIndex(visual);
2620                     else
2621                         d->target = d->logicalIndex(visual - 1);
2622                 } else {
2623                     d->target = d->section;
2624                 }
2625                 d->updateSectionIndicator(d->section, pos);
2626             }
2627             return;
2628         }
2629         case QHeaderViewPrivate::SelectSections: {
2630             int logical = logicalIndexAt(qMax(-d->offset, pos));
2631             if (logical == -1 && pos > 0)
2632                 logical = logicalIndex(d->lastVisibleVisualIndex());
2633             if (logical == d->pressed)
2634                 return; // nothing to do
2635             else if (d->pressed != -1)
2636                 updateSection(d->pressed);
2637             d->pressed = logical;
2638             if (d->clickableSections && logical != -1) {
2639                 emit sectionEntered(d->pressed);
2640                 updateSection(d->pressed);
2641             }
2642             return;
2643         }
2644         case QHeaderViewPrivate::NoState: {
2645 #ifndef QT_NO_CURSOR
2646             int handle = d->sectionHandleAt(pos);
2647             bool hasCursor = testAttribute(Qt::WA_SetCursor);
2648             if (handle != -1 && (sectionResizeMode(handle) == Interactive)) {
2649                 if (!hasCursor)
2650                     setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2651             } else {
2652                 if (hasCursor)
2653                     unsetCursor();
2654 #ifndef QT_NO_STATUSTIP
2655                 int logical = logicalIndexAt(pos);
2656                 QString statusTip;
2657                 if (logical != -1)
2658                     statusTip = d->model->headerData(logical, d->orientation, Qt::StatusTipRole).toString();
2659                 if (d->shouldClearStatusTip || !statusTip.isEmpty()) {
2660                     QStatusTipEvent tip(statusTip);
2661                     QCoreApplication::sendEvent(d->parent ? d->parent : this, &tip);
2662                     d->shouldClearStatusTip = !statusTip.isEmpty();
2663                 }
2664 #endif // !QT_NO_STATUSTIP
2665             }
2666 #endif
2667             return;
2668         }
2669         default:
2670             break;
2671     }
2672 }
2673 
2674 /*!
2675   \reimp
2676 */
2677 
mouseReleaseEvent(QMouseEvent * e)2678 void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2679 {
2680     Q_D(QHeaderView);
2681     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2682     switch (d->state) {
2683     case QHeaderViewPrivate::MoveSection:
2684         if (true
2685 #if QT_CONFIG(label)
2686             && !d->sectionIndicator->isHidden()
2687 #endif
2688       ) { // moving
2689             int from = visualIndex(d->section);
2690             Q_ASSERT(from != -1);
2691             int to = visualIndex(d->target);
2692             Q_ASSERT(to != -1);
2693             moveSection(from, to);
2694             d->section = d->target = -1;
2695             d->updateSectionIndicator(d->section, pos);
2696             break;
2697         } // not moving
2698         Q_FALLTHROUGH();
2699     case QHeaderViewPrivate::SelectSections:
2700         if (!d->clickableSections) {
2701             int section = logicalIndexAt(pos);
2702             updateSection(section);
2703         }
2704         Q_FALLTHROUGH();
2705     case QHeaderViewPrivate::NoState:
2706         if (d->clickableSections) {
2707             int section = logicalIndexAt(pos);
2708             if (section != -1 && section == d->firstPressed) {
2709                 QRect firstPressedSectionRect;
2710                 switch (d->orientation) {
2711                 case Qt::Horizontal:
2712                     firstPressedSectionRect.setRect(sectionViewportPosition(d->firstPressed),
2713                                                     0,
2714                                                     sectionSize(d->firstPressed),
2715                                                     d->viewport->height());
2716                     break;
2717                 case Qt::Vertical:
2718                     firstPressedSectionRect.setRect(0,
2719                                                     sectionViewportPosition(d->firstPressed),
2720                                                     d->viewport->width(),
2721                                                     sectionSize(d->firstPressed));
2722                     break;
2723                 };
2724 
2725                 if (firstPressedSectionRect.contains(e->pos())) {
2726                     d->flipSortIndicator(section);
2727                     emit sectionClicked(section);
2728                 }
2729             }
2730             if (d->pressed != -1)
2731                 updateSection(d->pressed);
2732         }
2733         break;
2734     case QHeaderViewPrivate::ResizeSection:
2735         d->originalSize = -1;
2736         d->clearCascadingSections();
2737         break;
2738     default:
2739         break;
2740     }
2741     d->state = QHeaderViewPrivate::NoState;
2742     d->firstPressed = d->pressed = -1;
2743 }
2744 
2745 /*!
2746   \reimp
2747 */
2748 
mouseDoubleClickEvent(QMouseEvent * e)2749 void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2750 {
2751     Q_D(QHeaderView);
2752     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2753     int handle = d->sectionHandleAt(pos);
2754     if (handle > -1 && sectionResizeMode(handle) == Interactive) {
2755         emit sectionHandleDoubleClicked(handle);
2756 #ifndef QT_NO_CURSOR
2757         Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2758                                       ? Qt::SplitHCursor : Qt::SplitVCursor;
2759         if (cursor().shape() == splitCursor) {
2760             // signal handlers may have changed the section size
2761             handle = d->sectionHandleAt(pos);
2762             if (!(handle > -1 && sectionResizeMode(handle) == Interactive))
2763                 setCursor(Qt::ArrowCursor);
2764         }
2765 #endif
2766     } else {
2767         emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2768     }
2769 }
2770 
2771 /*!
2772   \reimp
2773 */
2774 
viewportEvent(QEvent * e)2775 bool QHeaderView::viewportEvent(QEvent *e)
2776 {
2777     Q_D(QHeaderView);
2778     switch (e->type()) {
2779 #ifndef QT_NO_TOOLTIP
2780     case QEvent::ToolTip: {
2781         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2782         int logical = logicalIndexAt(he->pos());
2783         if (logical != -1) {
2784             QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2785             if (variant.isValid()) {
2786                 QToolTip::showText(he->globalPos(), variant.toString(), this);
2787                 return true;
2788             }
2789         }
2790         break; }
2791 #endif
2792 #if QT_CONFIG(whatsthis)
2793     case QEvent::QueryWhatsThis: {
2794         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2795         int logical = logicalIndexAt(he->pos());
2796         if (logical != -1
2797             && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2798             return true;
2799         break; }
2800     case QEvent::WhatsThis: {
2801         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2802         int logical = logicalIndexAt(he->pos());
2803         if (logical != -1) {
2804              QVariant whatsthis = d->model->headerData(logical, d->orientation,
2805                                                       Qt::WhatsThisRole);
2806              if (whatsthis.isValid()) {
2807                  QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2808                  return true;
2809              }
2810         }
2811         break; }
2812 #endif // QT_CONFIG(whatsthis)
2813 #if QT_CONFIG(statustip)
2814     case QEvent::StatusTip: {
2815         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2816         int logical = logicalIndexAt(he->pos());
2817         if (logical != -1) {
2818             QString statustip = d->model->headerData(logical, d->orientation,
2819                                                     Qt::StatusTipRole).toString();
2820             if (!statustip.isEmpty())
2821                 setStatusTip(statustip);
2822         }
2823         return true; }
2824 #endif // QT_CONFIG(statustip)
2825     case QEvent::Resize:
2826     case QEvent::FontChange:
2827     case QEvent::StyleChange:
2828         d->invalidateCachedSizeHint();
2829         Q_FALLTHROUGH();
2830     case QEvent::Hide:
2831     case QEvent::Show: {
2832         QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
2833         if (parent && parent->isVisible()) // Only resize if we have a visible parent
2834             resizeSections();
2835         emit geometriesChanged();
2836         break;}
2837     case QEvent::ContextMenu: {
2838         d->state = QHeaderViewPrivate::NoState;
2839         d->pressed = d->section = d->target = -1;
2840         d->updateSectionIndicator(d->section, -1);
2841         break; }
2842     case QEvent::Wheel: {
2843         QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2844         if (asa)
2845             return QCoreApplication::sendEvent(asa->viewport(), e);
2846         break; }
2847     default:
2848         break;
2849     }
2850     return QAbstractItemView::viewportEvent(e);
2851 }
2852 
2853 /*!
2854     Paints the section specified by the given \a logicalIndex, using the given
2855     \a painter and \a rect.
2856 
2857     Normally, you do not have to call this function.
2858 */
2859 
paintSection(QPainter * painter,const QRect & rect,int logicalIndex) const2860 void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2861 {
2862     Q_D(const QHeaderView);
2863     if (!rect.isValid())
2864         return;
2865     // get the state of the section
2866     QStyleOptionHeader opt;
2867     initStyleOption(&opt);
2868     QStyle::State state = QStyle::State_None;
2869     if (isEnabled())
2870         state |= QStyle::State_Enabled;
2871     if (window()->isActiveWindow())
2872         state |= QStyle::State_Active;
2873     if (d->clickableSections) {
2874         if (logicalIndex == d->hover)
2875             state |= QStyle::State_MouseOver;
2876         if (logicalIndex == d->pressed)
2877             state |= QStyle::State_Sunken;
2878         else if (d->highlightSelected) {
2879             if (d->sectionIntersectsSelection(logicalIndex))
2880                 state |= QStyle::State_On;
2881             if (d->isSectionSelected(logicalIndex))
2882                 state |= QStyle::State_Sunken;
2883         }
2884 
2885     }
2886     if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2887         opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2888                             ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2889 
2890     // setup the style options structure
2891     QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2892                                                   Qt::TextAlignmentRole);
2893     opt.rect = rect;
2894     opt.section = logicalIndex;
2895     opt.state |= state;
2896     opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2897                                       ? Qt::Alignment(textAlignment.toInt())
2898                                       : d->defaultAlignment);
2899 
2900     opt.iconAlignment = Qt::AlignVCenter;
2901     opt.text = d->model->headerData(logicalIndex, d->orientation,
2902                                     Qt::DisplayRole).toString();
2903 
2904     int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this);
2905 
2906     const Qt::Alignment headerArrowAlignment = static_cast<Qt::Alignment>(style()->styleHint(QStyle::SH_Header_ArrowAlignment, nullptr, this));
2907     const bool isHeaderArrowOnTheSide = headerArrowAlignment & Qt::AlignVCenter;
2908     if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex && isHeaderArrowOnTheSide)
2909         margin += style()->pixelMetric(QStyle::PM_HeaderMarkSize, nullptr, this);
2910 
2911     const QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2912                                                   Qt::DecorationRole);
2913     opt.icon = qvariant_cast<QIcon>(variant);
2914     if (opt.icon.isNull())
2915         opt.icon = qvariant_cast<QPixmap>(variant);
2916     if (!opt.icon.isNull()) // see CT_HeaderSection
2917         margin += style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this) +
2918                   style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this);
2919 
2920     if (d->textElideMode != Qt::ElideNone) {
2921         const QRect textRect = style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this);
2922         opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode, textRect.width() - margin);
2923     }
2924 
2925     QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2926                                                     Qt::ForegroundRole);
2927     if (foregroundBrush.canConvert<QBrush>())
2928         opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2929 
2930     QPointF oldBO = painter->brushOrigin();
2931     QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2932                                                     Qt::BackgroundRole);
2933     if (backgroundBrush.canConvert<QBrush>()) {
2934         opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2935         opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2936         painter->setBrushOrigin(opt.rect.topLeft());
2937     }
2938 
2939     // the section position
2940     int visual = visualIndex(logicalIndex);
2941     Q_ASSERT(visual != -1);
2942     bool first = d->isFirstVisibleSection(visual);
2943     bool last = d->isLastVisibleSection(visual);
2944     if (first && last)
2945         opt.position = QStyleOptionHeader::OnlyOneSection;
2946     else if (first)
2947         opt.position = d->reverse() ? QStyleOptionHeader::End : QStyleOptionHeader::Beginning;
2948     else if (last)
2949         opt.position = d->reverse() ? QStyleOptionHeader::Beginning : QStyleOptionHeader::End;
2950     else
2951         opt.position = QStyleOptionHeader::Middle;
2952     opt.orientation = d->orientation;
2953     // the selected position
2954     bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
2955     bool nextSelected =  d->isSectionSelected(this->logicalIndex(visual + 1));
2956     if (previousSelected && nextSelected)
2957         opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
2958     else if (previousSelected)
2959         opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
2960     else if (nextSelected)
2961         opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
2962     else
2963         opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
2964     // draw the section
2965     style()->drawControl(QStyle::CE_Header, &opt, painter, this);
2966 
2967     painter->setBrushOrigin(oldBO);
2968 }
2969 
2970 /*!
2971     Returns the size of the contents of the section specified by the given
2972     \a logicalIndex.
2973 
2974     \sa defaultSectionSize()
2975 */
2976 
sectionSizeFromContents(int logicalIndex) const2977 QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
2978 {
2979     Q_D(const QHeaderView);
2980     Q_ASSERT(logicalIndex >= 0);
2981 
2982     ensurePolished();
2983 
2984     // use SizeHintRole
2985     QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
2986     if (variant.isValid())
2987         return qvariant_cast<QSize>(variant);
2988 
2989     // otherwise use the contents
2990     QStyleOptionHeader opt;
2991     initStyleOption(&opt);
2992     opt.section = logicalIndex;
2993     QVariant var = d->model->headerData(logicalIndex, d->orientation,
2994                                             Qt::FontRole);
2995     QFont fnt;
2996     if (var.isValid() && var.canConvert<QFont>())
2997         fnt = qvariant_cast<QFont>(var);
2998     else
2999         fnt = font();
3000     fnt.setBold(true);
3001     opt.fontMetrics = QFontMetrics(fnt);
3002     opt.text = d->model->headerData(logicalIndex, d->orientation,
3003                                     Qt::DisplayRole).toString();
3004     variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
3005     opt.icon = qvariant_cast<QIcon>(variant);
3006     if (opt.icon.isNull())
3007         opt.icon = qvariant_cast<QPixmap>(variant);
3008     if (isSortIndicatorShown())
3009         opt.sortIndicator = QStyleOptionHeader::SortDown;
3010     return style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
3011 }
3012 
3013 /*!
3014     Returns the horizontal offset of the header. This is 0 for vertical
3015     headers.
3016 
3017     \sa offset()
3018 */
3019 
horizontalOffset() const3020 int QHeaderView::horizontalOffset() const
3021 {
3022     Q_D(const QHeaderView);
3023     if (d->orientation == Qt::Horizontal)
3024         return d->offset;
3025     return 0;
3026 }
3027 
3028 /*!
3029     Returns the vertical offset of the header. This is 0 for horizontal
3030     headers.
3031 
3032     \sa offset()
3033 */
3034 
verticalOffset() const3035 int QHeaderView::verticalOffset() const
3036 {
3037     Q_D(const QHeaderView);
3038     if (d->orientation == Qt::Vertical)
3039         return d->offset;
3040     return 0;
3041 }
3042 
3043 /*!
3044     \reimp
3045     \internal
3046 */
3047 
updateGeometries()3048 void QHeaderView::updateGeometries()
3049 {
3050     Q_D(QHeaderView);
3051     d->layoutChildren();
3052     if (d->hasAutoResizeSections())
3053         d->doDelayedResizeSections();
3054 }
3055 
3056 /*!
3057     \reimp
3058     \internal
3059 */
3060 
scrollContentsBy(int dx,int dy)3061 void QHeaderView::scrollContentsBy(int dx, int dy)
3062 {
3063     Q_D(QHeaderView);
3064     d->scrollDirtyRegion(dx, dy);
3065 }
3066 
3067 /*!
3068     \reimp
3069     \internal
3070 */
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles)3071 void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
3072 {
3073     Q_D(QHeaderView);
3074     if (!roles.isEmpty()) {
3075         const auto doesRoleAffectSize = [](int role) -> bool {
3076             switch (role) {
3077             case Qt::DisplayRole:
3078             case Qt::DecorationRole:
3079             case Qt::SizeHintRole:
3080             case Qt::FontRole:
3081                 return true;
3082             default:
3083                 // who knows what a subclass or custom style might do
3084                 return role >= Qt::UserRole;
3085             }
3086         };
3087         if (std::none_of(roles.begin(), roles.end(), doesRoleAffectSize))
3088             return;
3089     }
3090     d->invalidateCachedSizeHint();
3091     if (d->hasAutoResizeSections()) {
3092         bool resizeRequired = d->globalResizeMode == ResizeToContents;
3093         int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
3094         int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
3095         for (int i = first; i <= last && !resizeRequired; ++i)
3096             resizeRequired = (sectionResizeMode(i) == ResizeToContents);
3097         if (resizeRequired)
3098             d->doDelayedResizeSections();
3099     }
3100 }
3101 
3102 /*!
3103     \reimp
3104     \internal
3105 
3106     Empty implementation because the header doesn't show QModelIndex items.
3107 */
rowsInserted(const QModelIndex &,int,int)3108 void QHeaderView::rowsInserted(const QModelIndex &, int, int)
3109 {
3110     // do nothing
3111 }
3112 
3113 /*!
3114     \reimp
3115     \internal
3116 
3117     Empty implementation because the header doesn't show QModelIndex items.
3118 */
3119 
visualRect(const QModelIndex &) const3120 QRect QHeaderView::visualRect(const QModelIndex &) const
3121 {
3122     return QRect();
3123 }
3124 
3125 /*!
3126     \reimp
3127     \internal
3128 
3129     Empty implementation because the header doesn't show QModelIndex items.
3130 */
3131 
scrollTo(const QModelIndex &,ScrollHint)3132 void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
3133 {
3134     // do nothing - the header only displays sections
3135 }
3136 
3137 /*!
3138     \reimp
3139     \internal
3140 
3141     Empty implementation because the header doesn't show QModelIndex items.
3142 */
3143 
indexAt(const QPoint &) const3144 QModelIndex QHeaderView::indexAt(const QPoint &) const
3145 {
3146     return QModelIndex();
3147 }
3148 
3149 /*!
3150     \reimp
3151     \internal
3152 
3153     Empty implementation because the header doesn't show QModelIndex items.
3154 */
3155 
isIndexHidden(const QModelIndex &) const3156 bool QHeaderView::isIndexHidden(const QModelIndex &) const
3157 {
3158     return true; // the header view has no items, just sections
3159 }
3160 
3161 /*!
3162     \reimp
3163     \internal
3164 
3165     Empty implementation because the header doesn't show QModelIndex items.
3166 */
3167 
moveCursor(CursorAction,Qt::KeyboardModifiers)3168 QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
3169 {
3170     return QModelIndex();
3171 }
3172 
3173 /*!
3174     \reimp
3175 
3176     Selects the items in the given \a rect according to the specified
3177     \a flags.
3178 
3179     The base class implementation does nothing.
3180 */
3181 
setSelection(const QRect &,QItemSelectionModel::SelectionFlags)3182 void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
3183 {
3184     // do nothing
3185 }
3186 
3187 /*!
3188     \internal
3189 */
3190 
visualRegionForSelection(const QItemSelection & selection) const3191 QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
3192 {
3193     Q_D(const QHeaderView);
3194     const int max = d->modelSectionCount();
3195 
3196     if (d->orientation == Qt::Horizontal) {
3197         int logicalLeft = max;
3198         int logicalRight = 0;
3199 
3200         if (d->visualIndices.empty()) {
3201             // If no reordered sections, skip redundant visual-to-logical transformations
3202             for (const auto &r : selection) {
3203                 if (r.parent().isValid() || !r.isValid())
3204                     continue; // we only know about toplevel items and we don't want invalid ranges
3205                 if (r.left() < logicalLeft)
3206                     logicalLeft = r.left();
3207                 if (r.right() > logicalRight)
3208                     logicalRight = r.right();
3209             }
3210         } else {
3211             int left = max;
3212             int right = 0;
3213             for (const auto &r : selection) {
3214                 if (r.parent().isValid() || !r.isValid())
3215                     continue; // we only know about toplevel items and we don't want invalid ranges
3216                 for (int k = r.left(); k <= r.right(); ++k) {
3217                     int visual = visualIndex(k);
3218                     if (visual == -1)   // in some cases users may change the selections
3219                         continue;       // before we have a chance to do the layout
3220                     if (visual < left)
3221                         left = visual;
3222                     if (visual > right)
3223                         right = visual;
3224                 }
3225             }
3226             logicalLeft = logicalIndex(left);
3227             logicalRight = logicalIndex(right);
3228         }
3229 
3230         if (logicalLeft < 0  || logicalLeft >= count() ||
3231             logicalRight < 0 || logicalRight >= count())
3232             return QRegion();
3233 
3234         int leftPos = sectionViewportPosition(logicalLeft);
3235         int rightPos = sectionViewportPosition(logicalRight);
3236         rightPos += sectionSize(logicalRight);
3237         return QRect(leftPos, 0, rightPos - leftPos, height());
3238     }
3239     // orientation() == Qt::Vertical
3240     int logicalTop = max;
3241     int logicalBottom = 0;
3242 
3243     if (d->visualIndices.empty()) {
3244         // If no reordered sections, skip redundant visual-to-logical transformations
3245         for (const auto &r : selection) {
3246             if (r.parent().isValid() || !r.isValid())
3247                 continue; // we only know about toplevel items and we don't want invalid ranges
3248             if (r.top() < logicalTop)
3249                 logicalTop = r.top();
3250             if (r.bottom() > logicalBottom)
3251                 logicalBottom = r.bottom();
3252         }
3253     } else {
3254         int top = max;
3255         int bottom = 0;
3256 
3257         for (const auto &r : selection) {
3258             if (r.parent().isValid() || !r.isValid())
3259                 continue; // we only know about toplevel items and we don't want invalid ranges
3260             for (int k = r.top(); k <= r.bottom(); ++k) {
3261                 int visual = visualIndex(k);
3262                 if (visual == -1)   // in some cases users may change the selections
3263                     continue;       // before we have a chance to do the layout
3264                 if (visual < top)
3265                     top = visual;
3266                 if (visual > bottom)
3267                     bottom = visual;
3268             }
3269         }
3270 
3271         logicalTop = logicalIndex(top);
3272         logicalBottom = logicalIndex(bottom);
3273     }
3274 
3275     if (logicalTop < 0 || logicalTop >= count() ||
3276         logicalBottom < 0 || logicalBottom >= count())
3277         return QRegion();
3278 
3279     int topPos = sectionViewportPosition(logicalTop);
3280     int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
3281 
3282     return QRect(0, topPos, width(), bottomPos - topPos);
3283 }
3284 
3285 
3286 // private implementation
3287 
sectionHandleAt(int position)3288 int QHeaderViewPrivate::sectionHandleAt(int position)
3289 {
3290     Q_Q(QHeaderView);
3291     int visual = q->visualIndexAt(position);
3292     if (visual == -1)
3293         return -1;
3294     int log = logicalIndex(visual);
3295     int pos = q->sectionViewportPosition(log);
3296     int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, q);
3297 
3298     bool atLeft = position < pos + grip;
3299     bool atRight = (position > pos + q->sectionSize(log) - grip);
3300     if (reverse())
3301         qSwap(atLeft, atRight);
3302 
3303     if (atLeft) {
3304         //grip at the beginning of the section
3305         while(visual > -1) {
3306             int logical = q->logicalIndex(--visual);
3307             if (!q->isSectionHidden(logical))
3308                 return logical;
3309         }
3310     } else if (atRight) {
3311         //grip at the end of the section
3312         return log;
3313     }
3314     return -1;
3315 }
3316 
setupSectionIndicator(int section,int position)3317 void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
3318 {
3319     Q_Q(QHeaderView);
3320 #if QT_CONFIG(label)
3321     if (!sectionIndicator) {
3322         sectionIndicator = new QLabel(viewport);
3323     }
3324 #endif
3325 
3326     int w, h;
3327     int p = q->sectionViewportPosition(section);
3328     if (orientation == Qt::Horizontal) {
3329         w = q->sectionSize(section);
3330         h = viewport->height();
3331     } else {
3332         w = viewport->width();
3333         h = q->sectionSize(section);
3334     }
3335 #if QT_CONFIG(label)
3336     sectionIndicator->resize(w, h);
3337 #endif
3338 
3339     const qreal pixmapDevicePixelRatio = q->devicePixelRatioF();
3340     QPixmap pm(QSize(w, h) * pixmapDevicePixelRatio);
3341     pm.setDevicePixelRatio(pixmapDevicePixelRatio);
3342     pm.fill(QColor(0, 0, 0, 45));
3343     QRect rect(0, 0, w, h);
3344 
3345     QPainter painter(&pm);
3346     const QVariant variant = model->headerData(section, orientation,
3347                                                Qt::FontRole);
3348     if (variant.isValid() && variant.canConvert<QFont>()) {
3349         const QFont sectionFont = qvariant_cast<QFont>(variant);
3350         painter.setFont(sectionFont);
3351     } else {
3352         painter.setFont(q->font());
3353     }
3354 
3355     painter.setOpacity(0.75);
3356     q->paintSection(&painter, rect, section);
3357     painter.end();
3358 
3359 #if QT_CONFIG(label)
3360     sectionIndicator->setPixmap(pm);
3361 #endif
3362     sectionIndicatorOffset = position - qMax(p, 0);
3363 }
3364 
updateSectionIndicator(int section,int position)3365 void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
3366 {
3367 #if QT_CONFIG(label)
3368     if (!sectionIndicator)
3369         return;
3370 
3371     if (section == -1 || target == -1) {
3372         sectionIndicator->hide();
3373         return;
3374     }
3375 
3376     if (orientation == Qt::Horizontal)
3377         sectionIndicator->move(position - sectionIndicatorOffset, 0);
3378     else
3379         sectionIndicator->move(0, position - sectionIndicatorOffset);
3380 
3381     sectionIndicator->show();
3382 #endif
3383 }
3384 
3385 /*!
3386     Initialize \a option with the values from this QHeaderView. This method is
3387     useful for subclasses when they need a QStyleOptionHeader, but do not want
3388     to fill in all the information themselves.
3389 
3390     \sa QStyleOption::initFrom()
3391 */
initStyleOption(QStyleOptionHeader * option) const3392 void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
3393 {
3394     Q_D(const QHeaderView);
3395     option->initFrom(this);
3396     option->state = QStyle::State_None | QStyle::State_Raised;
3397     option->orientation = d->orientation;
3398     if (d->orientation == Qt::Horizontal)
3399         option->state |= QStyle::State_Horizontal;
3400     if (isEnabled())
3401         option->state |= QStyle::State_Enabled;
3402     option->section = 0;
3403 }
3404 
isSectionSelected(int section) const3405 bool QHeaderViewPrivate::isSectionSelected(int section) const
3406 {
3407     int i = section * 2;
3408     if (i < 0 || i >= sectionSelected.count())
3409         return false;
3410     if (sectionSelected.testBit(i)) // if the value was cached
3411         return sectionSelected.testBit(i + 1);
3412     bool s = false;
3413     if (orientation == Qt::Horizontal)
3414         s = isColumnSelected(section);
3415     else
3416         s = isRowSelected(section);
3417     sectionSelected.setBit(i + 1, s); // selection state
3418     sectionSelected.setBit(i, true); // cache state
3419     return s;
3420 }
3421 
isFirstVisibleSection(int section) const3422 bool QHeaderViewPrivate::isFirstVisibleSection(int section) const
3423 {
3424     if (sectionStartposRecalc)
3425         recalcSectionStartPos();
3426     const SectionItem &item = sectionItems.at(section);
3427     return item.size > 0 && item.calculated_startpos == 0;
3428 }
3429 
isLastVisibleSection(int section) const3430 bool QHeaderViewPrivate::isLastVisibleSection(int section) const
3431 {
3432     if (sectionStartposRecalc)
3433         recalcSectionStartPos();
3434     const SectionItem &item = sectionItems.at(section);
3435     return item.size > 0 && item.calculatedEndPos() == length;
3436 }
3437 
3438 /*!
3439     \internal
3440     Returns the last visible (ie. not hidden) visual index
3441 */
lastVisibleVisualIndex() const3442 int QHeaderViewPrivate::lastVisibleVisualIndex() const
3443 {
3444     Q_Q(const QHeaderView);
3445     for (int visual = q->count()-1; visual >= 0; --visual) {
3446         if (!q->isSectionHidden(q->logicalIndex(visual)))
3447             return visual;
3448     }
3449 
3450     //default value if no section is actually visible
3451     return -1;
3452 }
3453 
restoreSizeOnPrevLastSection()3454 void QHeaderViewPrivate::restoreSizeOnPrevLastSection()
3455 {
3456     Q_Q(QHeaderView);
3457     if (lastSectionLogicalIdx < 0)
3458         return;
3459     int resizeLogIdx = lastSectionLogicalIdx;
3460     lastSectionLogicalIdx = -1; // We do not want resize to catch it as the last section.
3461     q->resizeSection(resizeLogIdx, lastSectionSize);
3462 }
3463 
setNewLastSection(int visualIndexForLastSection)3464 void QHeaderViewPrivate::setNewLastSection(int visualIndexForLastSection)
3465 {
3466     Q_Q(QHeaderView);
3467     lastSectionSize = -1;
3468     lastSectionLogicalIdx = q->logicalIndex(visualIndexForLastSection);
3469     lastSectionSize = headerSectionSize(visualIndexForLastSection); // pick size directly since ...
3470     //  q->sectionSize(lastSectionLogicalIdx) may do delayed resize and stretch it before we get the value.
3471 }
3472 
maybeRestorePrevLastSectionAndStretchLast()3473 void QHeaderViewPrivate::maybeRestorePrevLastSectionAndStretchLast()
3474 {
3475     Q_Q(const QHeaderView);
3476     if (!q->stretchLastSection())
3477         return;
3478 
3479     int nowLastVisualSection = lastVisibleVisualIndex();
3480     if (lastSectionLogicalIdx == q->logicalIndex(nowLastVisualSection))
3481         return;
3482 
3483     // restore old last section.
3484     restoreSizeOnPrevLastSection();
3485     setNewLastSection(nowLastVisualSection);
3486     doDelayedResizeSections(); // Do stretch of last section soon (but not now).
3487 }
3488 
3489 
3490 /*!
3491     \internal
3492     Go through and resize all of the sections applying stretchLastSection,
3493     manual stretches, sizes, and useGlobalMode.
3494 
3495     The different resize modes are:
3496     Interactive - the user decides the size
3497     Stretch - take up whatever space is left
3498     Fixed - the size is set programmatically outside the header
3499     ResizeToContentes - the size is set based on the contents of the row or column in the parent view
3500 
3501     The resize mode will not affect the last section if stretchLastSection is true.
3502 */
resizeSections(QHeaderView::ResizeMode globalMode,bool useGlobalMode)3503 void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
3504 {
3505     Q_Q(QHeaderView);
3506     //stop the timer in case it is delayed
3507     delayedResize.stop();
3508 
3509     executePostedLayout();
3510     if (sectionCount() == 0)
3511         return;
3512 
3513     if (resizeRecursionBlock)
3514         return;
3515     resizeRecursionBlock = true;
3516 
3517     invalidateCachedSizeHint();
3518     const int lastSectionVisualIdx = q->visualIndex(lastSectionLogicalIdx);
3519 
3520     // find stretchLastSection if we have it
3521     int stretchSection = -1;
3522     if (stretchLastSection && !useGlobalMode)
3523         stretchSection = lastSectionVisualIdx;
3524 
3525     // count up the number of stretched sections and how much space left for them
3526     int lengthToStretch = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
3527     int numberOfStretchedSections = 0;
3528     QList<int> section_sizes;
3529     for (int i = 0; i < sectionCount(); ++i) {
3530         if (isVisualIndexHidden(i))
3531             continue;
3532 
3533         QHeaderView::ResizeMode resizeMode;
3534         if (useGlobalMode && (i != stretchSection))
3535             resizeMode = globalMode;
3536         else
3537             resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i));
3538 
3539         if (resizeMode == QHeaderView::Stretch) {
3540             ++numberOfStretchedSections;
3541             section_sizes.append(headerSectionSize(i));
3542             continue;
3543         }
3544 
3545         // because it isn't stretch, determine its width and remove that from lengthToStretch
3546         int sectionSize = 0;
3547         if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
3548             sectionSize = qBound(q->minimumSectionSize(), headerSectionSize(i), q->maximumSectionSize());
3549         } else { // resizeMode == QHeaderView::ResizeToContents
3550             int logicalIndex = q->logicalIndex(i);
3551             sectionSize = qMax(viewSectionSizeHint(logicalIndex),
3552                                q->sectionSizeHint(logicalIndex));
3553         }
3554         sectionSize = qBound(q->minimumSectionSize(),
3555                              sectionSize,
3556                              q->maximumSectionSize());
3557 
3558         section_sizes.append(sectionSize);
3559         lengthToStretch -= sectionSize;
3560     }
3561 
3562     // calculate the new length for all of the stretched sections
3563     int stretchSectionLength = -1;
3564     int pixelReminder = 0;
3565     if (numberOfStretchedSections > 0 && lengthToStretch > 0) { // we have room to stretch in
3566         int hintLengthForEveryStretchedSection = lengthToStretch / numberOfStretchedSections;
3567         stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
3568         pixelReminder = lengthToStretch % numberOfStretchedSections;
3569     }
3570 
3571     // ### The code below would be nicer if it was cleaned up a bit (since spans has been replaced with items)
3572     int spanStartSection = 0;
3573     int previousSectionLength = 0;
3574 
3575     QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
3576 
3577     // resize each section along the total length
3578     for (int i = 0; i < sectionCount(); ++i) {
3579         int oldSectionLength = headerSectionSize(i);
3580         int newSectionLength = -1;
3581         QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
3582 
3583         if (isVisualIndexHidden(i)) {
3584             newSectionLength = 0;
3585         } else {
3586             QHeaderView::ResizeMode resizeMode;
3587             if (useGlobalMode)
3588                 resizeMode = globalMode;
3589             else
3590                 resizeMode = (i == stretchSection
3591                               ? QHeaderView::Stretch
3592                               : newSectionResizeMode);
3593             if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
3594                 if (i == lastSectionVisualIdx)
3595                     newSectionLength = qMax(stretchSectionLength, lastSectionSize);
3596                 else
3597                     newSectionLength = stretchSectionLength;
3598                 if (pixelReminder > 0) {
3599                     newSectionLength += 1;
3600                     --pixelReminder;
3601                 }
3602                 section_sizes.removeFirst();
3603             } else {
3604                 newSectionLength = section_sizes.takeFirst();
3605             }
3606         }
3607 
3608         //Q_ASSERT(newSectionLength > 0);
3609         if ((previousSectionResizeMode != newSectionResizeMode
3610             || previousSectionLength != newSectionLength) && i > 0) {
3611             int spanLength = (i - spanStartSection) * previousSectionLength;
3612             createSectionItems(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
3613             //Q_ASSERT(headerLength() == length);
3614             spanStartSection = i;
3615         }
3616 
3617         if (newSectionLength != oldSectionLength)
3618             emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
3619 
3620         previousSectionLength = newSectionLength;
3621         previousSectionResizeMode = newSectionResizeMode;
3622     }
3623 
3624     createSectionItems(spanStartSection, sectionCount() - 1,
3625                       (sectionCount() - spanStartSection) * previousSectionLength,
3626                       previousSectionResizeMode);
3627     //Q_ASSERT(headerLength() == length);
3628     resizeRecursionBlock = false;
3629     viewport->update();
3630 }
3631 
createSectionItems(int start,int end,int size,QHeaderView::ResizeMode mode)3632 void QHeaderViewPrivate::createSectionItems(int start, int end, int size, QHeaderView::ResizeMode mode)
3633 {
3634     int sizePerSection = size / (end - start + 1);
3635     if (end >= sectionItems.count()) {
3636         sectionItems.resize(end + 1);
3637         sectionStartposRecalc = true;
3638     }
3639     SectionItem *sectiondata = sectionItems.data();
3640     for (int i = start; i <= end; ++i) {
3641         length += (sizePerSection - sectiondata[i].size);
3642         sectionStartposRecalc |= (sectiondata[i].size != sizePerSection);
3643         sectiondata[i].size = sizePerSection;
3644         sectiondata[i].resizeMode = mode;
3645     }
3646 }
3647 
removeSectionsFromSectionItems(int start,int end)3648 void QHeaderViewPrivate::removeSectionsFromSectionItems(int start, int end)
3649 {
3650     // remove sections
3651     sectionStartposRecalc |= (end != sectionItems.count() - 1);
3652     int removedlength = 0;
3653     for (int u = start; u <= end; ++u)
3654         removedlength += sectionItems.at(u).size;
3655     length -= removedlength;
3656     sectionItems.remove(start, end - start + 1);
3657 }
3658 
clear()3659 void QHeaderViewPrivate::clear()
3660 {
3661     if (state != NoClear) {
3662         length = 0;
3663         visualIndices.clear();
3664         logicalIndices.clear();
3665         sectionSelected.clear();
3666         hiddenSectionSize.clear();
3667         sectionItems.clear();
3668         lastSectionLogicalIdx = -1;
3669         invalidateCachedSizeHint();
3670     }
3671 }
3672 
flipSortIndicator(int section)3673 void QHeaderViewPrivate::flipSortIndicator(int section)
3674 {
3675     Q_Q(QHeaderView);
3676     Qt::SortOrder sortOrder;
3677     if (sortIndicatorSection == section) {
3678         sortOrder = (sortIndicatorOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder;
3679     } else {
3680         const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole);
3681         if (value.canConvert(QMetaType::Int))
3682             sortOrder = static_cast<Qt::SortOrder>(value.toInt());
3683         else
3684             sortOrder = Qt::AscendingOrder;
3685     }
3686     q->setSortIndicator(section, sortOrder);
3687 }
3688 
cascadingResize(int visual,int newSize)3689 void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
3690 {
3691     Q_Q(QHeaderView);
3692     const int minimumSize = q->minimumSectionSize();
3693     const int oldSize = headerSectionSize(visual);
3694     int delta = newSize - oldSize;
3695 
3696     if (delta > 0) { // larger
3697         bool sectionResized = false;
3698 
3699         // restore old section sizes
3700         for (int i = firstCascadingSection; i < visual; ++i) {
3701             if (cascadingSectionSize.contains(i)) {
3702                 int currentSectionSize = headerSectionSize(i);
3703                 int originalSectionSize = cascadingSectionSize.value(i);
3704                 if (currentSectionSize < originalSectionSize) {
3705                     int newSectionSize = currentSectionSize + delta;
3706                     resizeSectionItem(i, currentSectionSize, newSectionSize);
3707                     if (newSectionSize >= originalSectionSize && false)
3708                         cascadingSectionSize.remove(i); // the section is now restored
3709                     sectionResized = true;
3710                     break;
3711                 }
3712             }
3713 
3714         }
3715 
3716         // resize the section
3717         if (!sectionResized) {
3718             newSize = qMax(newSize, minimumSize);
3719             if (oldSize != newSize)
3720                 resizeSectionItem(visual, oldSize, newSize);
3721         }
3722 
3723         // cascade the section size change
3724         for (int i = visual + 1; i < sectionCount(); ++i) {
3725             if (isVisualIndexHidden(i))
3726                 continue;
3727             if (!sectionIsCascadable(i))
3728                 continue;
3729             int currentSectionSize = headerSectionSize(i);
3730             if (currentSectionSize <= minimumSize)
3731                 continue;
3732             int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3733             //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
3734             resizeSectionItem(i, currentSectionSize, newSectionSize);
3735             saveCascadingSectionSize(i, currentSectionSize);
3736             delta = delta - (currentSectionSize - newSectionSize);
3737             //qDebug() << "new delta" << delta;
3738             //if (newSectionSize != minimumSize)
3739             if (delta <= 0)
3740                 break;
3741         }
3742     } else { // smaller
3743         bool sectionResized = false;
3744 
3745         // restore old section sizes
3746         for (int i = lastCascadingSection; i > visual; --i) {
3747             if (!cascadingSectionSize.contains(i))
3748                 continue;
3749             int currentSectionSize = headerSectionSize(i);
3750             int originalSectionSize = cascadingSectionSize.value(i);
3751             if (currentSectionSize >= originalSectionSize)
3752                 continue;
3753             int newSectionSize = currentSectionSize - delta;
3754             resizeSectionItem(i, currentSectionSize, newSectionSize);
3755             if (newSectionSize >= originalSectionSize && false) {
3756                 //qDebug() << "section" << i << "restored to" << originalSectionSize;
3757                 cascadingSectionSize.remove(i); // the section is now restored
3758             }
3759             sectionResized = true;
3760             break;
3761         }
3762 
3763         // resize the section
3764         resizeSectionItem(visual, oldSize, qMax(newSize, minimumSize));
3765 
3766         // cascade the section size change
3767         if (delta < 0 && newSize < minimumSize) {
3768             for (int i = visual - 1; i >= 0; --i) {
3769                 if (isVisualIndexHidden(i))
3770                     continue;
3771                 if (!sectionIsCascadable(i))
3772                     continue;
3773                 int sectionSize = headerSectionSize(i);
3774                 if (sectionSize <= minimumSize)
3775                     continue;
3776                 resizeSectionItem(i, sectionSize, qMax(sectionSize + delta, minimumSize));
3777                 saveCascadingSectionSize(i, sectionSize);
3778                 break;
3779             }
3780         }
3781 
3782         // let the next section get the space from the resized section
3783         if (!sectionResized) {
3784             for (int i = visual + 1; i < sectionCount(); ++i) {
3785                 if (isVisualIndexHidden(i))
3786                     continue;
3787                 if (!sectionIsCascadable(i))
3788                     continue;
3789                 int currentSectionSize = headerSectionSize(i);
3790                 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3791                 resizeSectionItem(i, currentSectionSize, newSectionSize);
3792                 break;
3793             }
3794         }
3795     }
3796 
3797     if (hasAutoResizeSections())
3798         doDelayedResizeSections();
3799 
3800     viewport->update();
3801 }
3802 
setDefaultSectionSize(int size)3803 void QHeaderViewPrivate::setDefaultSectionSize(int size)
3804 {
3805     Q_Q(QHeaderView);
3806     size = qBound(q->minimumSectionSize(), size, q->maximumSectionSize());
3807     executePostedLayout();
3808     invalidateCachedSizeHint();
3809     defaultSectionSize = size;
3810     customDefaultSectionSize = true;
3811     if (state == QHeaderViewPrivate::ResizeSection)
3812         preventCursorChangeInSetOffset = true;
3813     for (int i = 0; i < sectionItems.count(); ++i) {
3814         QHeaderViewPrivate::SectionItem &section = sectionItems[i];
3815         if (hiddenSectionSize.isEmpty() || !isVisualIndexHidden(i)) { // resize on not hidden.
3816             const int newSize = size;
3817             if (newSize != section.size) {
3818                 length += newSize - section.size; //the whole length is changed
3819                 const int oldSectionSize = section.sectionSize();
3820                 section.size = size;
3821                 emit q->sectionResized(logicalIndex(i), oldSectionSize, size);
3822             }
3823         }
3824     }
3825     sectionStartposRecalc = true;
3826     if (hasAutoResizeSections())
3827         doDelayedResizeSections();
3828     viewport->update();
3829 }
3830 
updateDefaultSectionSizeFromStyle()3831 void QHeaderViewPrivate::updateDefaultSectionSizeFromStyle()
3832 {
3833     Q_Q(QHeaderView);
3834     if (orientation == Qt::Horizontal) {
3835         defaultSectionSize = q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeHorizontal, nullptr, q);
3836     } else {
3837         defaultSectionSize = qMax(q->minimumSectionSize(),
3838                                   q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeVertical, nullptr, q));
3839     }
3840 }
3841 
recalcSectionStartPos() const3842 void QHeaderViewPrivate::recalcSectionStartPos() const // linear (but fast)
3843 {
3844     int pixelpos = 0;
3845     for (const SectionItem &i : sectionItems) {
3846         i.calculated_startpos = pixelpos; // write into const mutable
3847         pixelpos += i.size;
3848     }
3849     sectionStartposRecalc = false;
3850 }
3851 
resizeSectionItem(int visualIndex,int oldSize,int newSize)3852 void QHeaderViewPrivate::resizeSectionItem(int visualIndex, int oldSize, int newSize)
3853 {
3854     Q_Q(QHeaderView);
3855     QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
3856     createSectionItems(visualIndex, visualIndex, newSize, mode);
3857     emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
3858 }
3859 
headerSectionSize(int visual) const3860 int QHeaderViewPrivate::headerSectionSize(int visual) const
3861 {
3862     if (visual < sectionCount() && visual >= 0)
3863         return sectionItems.at(visual).sectionSize();
3864     return -1;
3865 }
3866 
headerSectionPosition(int visual) const3867 int QHeaderViewPrivate::headerSectionPosition(int visual) const
3868 {
3869     if (visual < sectionCount() && visual >= 0) {
3870         if (sectionStartposRecalc)
3871             recalcSectionStartPos();
3872         return sectionItems.at(visual).calculated_startpos;
3873     }
3874     return -1;
3875 }
3876 
headerVisualIndexAt(int position) const3877 int QHeaderViewPrivate::headerVisualIndexAt(int position) const
3878 {
3879     if (sectionStartposRecalc)
3880         recalcSectionStartPos();
3881     int startidx = 0;
3882     int endidx = sectionItems.count() - 1;
3883     while (startidx <= endidx) {
3884         int middle = (endidx + startidx) / 2;
3885         if (sectionItems.at(middle).calculated_startpos > position) {
3886             endidx = middle - 1;
3887         } else {
3888             if (sectionItems.at(middle).calculatedEndPos() <= position)
3889                 startidx = middle + 1;
3890             else // we found it.
3891                 return middle;
3892         }
3893     }
3894     return -1;
3895 }
3896 
setHeaderSectionResizeMode(int visual,QHeaderView::ResizeMode mode)3897 void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
3898 {
3899     int size = headerSectionSize(visual);
3900     createSectionItems(visual, visual, size, mode);
3901 }
3902 
headerSectionResizeMode(int visual) const3903 QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
3904 {
3905     if (visual < 0 || visual >= sectionItems.count())
3906         return globalResizeMode;
3907     return static_cast<QHeaderView::ResizeMode>(sectionItems.at(visual).resizeMode);
3908 }
3909 
setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)3910 void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
3911 {
3912     globalResizeMode = mode;
3913     for (int i = 0; i < sectionItems.count(); ++i)
3914         sectionItems[i].resizeMode = mode;
3915 }
3916 
viewSectionSizeHint(int logical) const3917 int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
3918 {
3919     if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) {
3920         return (orientation == Qt::Horizontal
3921                 ? view->sizeHintForColumn(logical)
3922                 : view->sizeHintForRow(logical));
3923     }
3924     return 0;
3925 }
3926 
adjustedVisualIndex(int visualIndex) const3927 int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
3928 {
3929     if (!hiddenSectionSize.isEmpty()) {
3930         int adjustedVisualIndex = visualIndex;
3931         int currentVisualIndex = 0;
3932         for (int i = 0; i < sectionItems.count(); ++i) {
3933             if (isVisualIndexHidden(i))
3934                 ++adjustedVisualIndex;
3935             else
3936                 ++currentVisualIndex;
3937             if (currentVisualIndex >= visualIndex)
3938                 break;
3939         }
3940         visualIndex = adjustedVisualIndex;
3941     }
3942     return visualIndex;
3943 }
3944 
setScrollOffset(const QScrollBar * scrollBar,QAbstractItemView::ScrollMode scrollMode)3945 void QHeaderViewPrivate::setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode)
3946 {
3947     Q_Q(QHeaderView);
3948     if (scrollMode == QAbstractItemView::ScrollPerItem) {
3949         if (scrollBar->maximum() > 0 && scrollBar->value() == scrollBar->maximum())
3950             q->setOffsetToLastSection();
3951         else
3952             q->setOffsetToSectionPosition(scrollBar->value());
3953     } else {
3954         q->setOffset(scrollBar->value());
3955     }
3956 }
3957 
3958 #ifndef QT_NO_DATASTREAM
write(QDataStream & out) const3959 void QHeaderViewPrivate::write(QDataStream &out) const
3960 {
3961     out << int(orientation);
3962     out << int(sortIndicatorOrder);
3963     out << sortIndicatorSection;
3964     out << sortIndicatorShown;
3965 
3966     out << visualIndices;
3967     out << logicalIndices;
3968 
3969     out << sectionsHiddenToBitVector();
3970     out << hiddenSectionSize;
3971 
3972     out << length;
3973     out << sectionCount();
3974     out << movableSections;
3975     out << clickableSections;
3976     out << highlightSelected;
3977     out << stretchLastSection;
3978     out << cascadingResizing;
3979     out << stretchSections;
3980     out << contentsSections;
3981     out << defaultSectionSize;
3982     out << minimumSectionSize;
3983 
3984     out << int(defaultAlignment);
3985     out << int(globalResizeMode);
3986 
3987     out << sectionItems;
3988     out << resizeContentsPrecision;
3989     out << customDefaultSectionSize;
3990     out << lastSectionSize;
3991 }
3992 
read(QDataStream & in)3993 bool QHeaderViewPrivate::read(QDataStream &in)
3994 {
3995     Q_Q(QHeaderView);
3996     int orient, order, align, global;
3997     int sortIndicatorSectionIn;
3998     bool sortIndicatorShownIn;
3999     int lengthIn;
4000     QVector<int> visualIndicesIn;
4001     QVector<int> logicalIndicesIn;
4002     QHash<int, int> hiddenSectionSizeIn;
4003     bool movableSectionsIn;
4004     bool clickableSectionsIn;
4005     bool highlightSelectedIn;
4006     bool stretchLastSectionIn;
4007     bool cascadingResizingIn;
4008     int stretchSectionsIn;
4009     int contentsSectionsIn;
4010     int defaultSectionSizeIn;
4011     int minimumSectionSizeIn;
4012     QVector<SectionItem> sectionItemsIn;
4013 
4014     in >> orient;
4015     in >> order;
4016 
4017     in >> sortIndicatorSectionIn;
4018     in >> sortIndicatorShownIn;
4019 
4020     in >> visualIndicesIn;
4021     in >> logicalIndicesIn;
4022 
4023     QBitArray sectionHidden;
4024     in >> sectionHidden;
4025     in >> hiddenSectionSizeIn;
4026     in >> lengthIn;
4027 
4028     int unusedSectionCount; // For compatibility
4029     in >> unusedSectionCount;
4030 
4031     if (in.status() != QDataStream::Ok || lengthIn < 0)
4032         return false;
4033 
4034     in >> movableSectionsIn;
4035     in >> clickableSectionsIn;
4036     in >> highlightSelectedIn;
4037     in >> stretchLastSectionIn;
4038     in >> cascadingResizingIn;
4039     in >> stretchSectionsIn;
4040     in >> contentsSectionsIn;
4041     in >> defaultSectionSizeIn;
4042     in >> minimumSectionSizeIn;
4043 
4044     in >> align;
4045 
4046     in >> global;
4047 
4048     in >> sectionItemsIn;
4049     // In Qt4 we had a vector of spans where one span could hold information on more sections.
4050     // Now we have an itemvector where one items contains information about one section
4051     // For backward compatibility with Qt4 we do the following
4052     QVector<SectionItem> newSectionItems;
4053     for (int u = 0; u < sectionItemsIn.count(); ++u) {
4054         int count = sectionItemsIn.at(u).tmpDataStreamSectionCount;
4055         if (count > 1)
4056             sectionItemsIn[u].size /= count;
4057         for (int n = 0; n < count; ++n)
4058             newSectionItems.append(sectionItemsIn[u]);
4059     }
4060 
4061     int sectionItemsLengthTotal = 0;
4062     for (const SectionItem &section : qAsConst(newSectionItems))
4063         sectionItemsLengthTotal += section.size;
4064     if (sectionItemsLengthTotal != lengthIn)
4065         return false;
4066 
4067     const int currentCount = (orient == Qt::Horizontal ? model->columnCount(root) : model->rowCount(root));
4068     if (newSectionItems.count() < currentCount) {
4069         // we have sections not in the saved state, give them default settings
4070         if (!visualIndicesIn.isEmpty() && !logicalIndicesIn.isEmpty()) {
4071             for (int i = newSectionItems.count(); i < currentCount; ++i) {
4072                 visualIndicesIn.append(i);
4073                 logicalIndicesIn.append(i);
4074             }
4075         }
4076         const int insertCount = currentCount - newSectionItems.count();
4077         const int insertLength = defaultSectionSizeIn * insertCount;
4078         lengthIn += insertLength;
4079         SectionItem section(defaultSectionSizeIn, globalResizeMode);
4080         newSectionItems.insert(newSectionItems.count(), insertCount, section); // append
4081     }
4082 
4083     orientation = static_cast<Qt::Orientation>(orient);
4084     sortIndicatorOrder = static_cast<Qt::SortOrder>(order);
4085     sortIndicatorSection = sortIndicatorSectionIn;
4086     sortIndicatorShown = sortIndicatorShownIn;
4087     visualIndices = visualIndicesIn;
4088     logicalIndices = logicalIndicesIn;
4089     hiddenSectionSize = hiddenSectionSizeIn;
4090     length = lengthIn;
4091 
4092     movableSections = movableSectionsIn;
4093     clickableSections = clickableSectionsIn;
4094     highlightSelected = highlightSelectedIn;
4095     stretchLastSection = stretchLastSectionIn;
4096     cascadingResizing = cascadingResizingIn;
4097     stretchSections = stretchSectionsIn;
4098     contentsSections = contentsSectionsIn;
4099     defaultSectionSize = defaultSectionSizeIn;
4100     minimumSectionSize = minimumSectionSizeIn;
4101 
4102     defaultAlignment = Qt::Alignment(align);
4103     globalResizeMode = static_cast<QHeaderView::ResizeMode>(global);
4104 
4105     sectionItems = newSectionItems;
4106     setHiddenSectionsFromBitVector(sectionHidden);
4107     recalcSectionStartPos();
4108 
4109     int tmpint;
4110     in >> tmpint;
4111     if (in.status() == QDataStream::Ok)  // we haven't read past end
4112         resizeContentsPrecision = tmpint;
4113 
4114     bool tmpbool;
4115     in >> tmpbool;
4116     if (in.status() == QDataStream::Ok) {  // we haven't read past end
4117         customDefaultSectionSize = tmpbool;
4118         if (!customDefaultSectionSize)
4119             updateDefaultSectionSizeFromStyle();
4120     }
4121 
4122     lastSectionSize = -1;
4123     int inLastSectionSize;
4124     in >> inLastSectionSize;
4125     if (in.status() == QDataStream::Ok)
4126         lastSectionSize = inLastSectionSize;
4127 
4128     lastSectionLogicalIdx = -1;
4129     if (stretchLastSection) {
4130         lastSectionLogicalIdx = q->logicalIndex(lastVisibleVisualIndex());
4131         doDelayedResizeSections();
4132     }
4133 
4134     return true;
4135 }
4136 
4137 #endif // QT_NO_DATASTREAM
4138 
4139 QT_END_NAMESPACE
4140 
4141 #include "moc_qheaderview.cpp"
4142