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

source code of qtbase/src/widgets/itemviews/qheaderview.cpp