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