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