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