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