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 // bail out on no section count, if recursive or if all sections should be same size (see above)
3686 if (sectionCount() == 0 || resizeRecursionBlock || noSectionMemoryUsage())
3687 return;
3688
3689 resizeRecursionBlock = true;
3690
3691 invalidateCachedSizeHint();
3692 const int lastSectionVisualIdx = q->visualIndex(lastSectionLogicalIdx);
3693
3694 // find stretchLastSection if we have it
3695 int stretchSection = -1;
3696 if (stretchLastSection && !useGlobalMode)
3697 stretchSection = lastSectionVisualIdx;
3698
3699 // count up the number of stretched sections and how much space left for them
3700 int lengthToStretch = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
3701 int numberOfStretchedSections = 0;
3702 QList<int> section_sizes;
3703 for (int i = 0; i < sectionCount(); ++i) {
3704 if (isVisualIndexHidden(i))
3705 continue;
3706
3707 QHeaderView::ResizeMode resizeMode;
3708 if (useGlobalMode && (i != stretchSection))
3709 resizeMode = globalMode;
3710 else
3711 resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i));
3712
3713 if (resizeMode == QHeaderView::Stretch) {
3714 ++numberOfStretchedSections;
3715 section_sizes.append(headerSectionSize(i));
3716 continue;
3717 }
3718
3719 // because it isn't stretch, determine its width and remove that from lengthToStretch
3720 int sectionSize = 0;
3721 if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
3722 sectionSize = qBound(q->minimumSectionSize(), headerSectionSize(i), q->maximumSectionSize());
3723 } else { // resizeMode == QHeaderView::ResizeToContents
3724 int logicalIndex = q->logicalIndex(i);
3725 sectionSize = qMax(viewSectionSizeHint(logicalIndex),
3726 q->sectionSizeHint(logicalIndex));
3727 }
3728 sectionSize = qBound(q->minimumSectionSize(),
3729 sectionSize,
3730 q->maximumSectionSize());
3731
3732 section_sizes.append(sectionSize);
3733 lengthToStretch -= sectionSize;
3734 }
3735
3736 // calculate the new length for all of the stretched sections
3737 int stretchSectionLength = -1;
3738 int pixelReminder = 0;
3739 if (numberOfStretchedSections > 0 && lengthToStretch > 0) { // we have room to stretch in
3740 int hintLengthForEveryStretchedSection = lengthToStretch / numberOfStretchedSections;
3741 stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
3742 pixelReminder = lengthToStretch % numberOfStretchedSections;
3743 }
3744
3745 // ### The code below would be nicer if it was cleaned up a bit (since spans has been replaced with items)
3746 int spanStartSection = 0;
3747 int previousSectionLength = 0;
3748
3749 QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
3750
3751 // resize each section along the total length
3752 for (int i = 0; i < sectionCount(); ++i) {
3753 int oldSectionLength = headerSectionSize(i);
3754 int newSectionLength = -1;
3755 QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
3756
3757 if (isVisualIndexHidden(i)) {
3758 newSectionLength = 0;
3759 } else {
3760 QHeaderView::ResizeMode resizeMode;
3761 if (useGlobalMode)
3762 resizeMode = globalMode;
3763 else
3764 resizeMode = (i == stretchSection
3765 ? QHeaderView::Stretch
3766 : newSectionResizeMode);
3767 if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
3768 if (i == lastSectionVisualIdx)
3769 newSectionLength = qMax(stretchSectionLength, lastSectionSize);
3770 else
3771 newSectionLength = stretchSectionLength;
3772 if (pixelReminder > 0) {
3773 newSectionLength += 1;
3774 --pixelReminder;
3775 }
3776 section_sizes.removeFirst();
3777 } else {
3778 newSectionLength = section_sizes.takeFirst();
3779 }
3780 }
3781
3782 //Q_ASSERT(newSectionLength > 0);
3783 if ((previousSectionResizeMode != newSectionResizeMode
3784 || previousSectionLength != newSectionLength) && i > 0) {
3785 createSectionItems(spanStartSection, i - 1, previousSectionLength, previousSectionResizeMode);
3786 //Q_ASSERT(headerLength() == length);
3787 spanStartSection = i;
3788 }
3789
3790 if (newSectionLength != oldSectionLength)
3791 emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
3792
3793 previousSectionLength = newSectionLength;
3794 previousSectionResizeMode = newSectionResizeMode;
3795 }
3796
3797 createSectionItems(spanStartSection, sectionCount() - 1,
3798 previousSectionLength, previousSectionResizeMode);
3799 //Q_ASSERT(headerLength() == length);
3800 resizeRecursionBlock = false;
3801 viewport->update();
3802}
3803
3804void QHeaderViewPrivate::createSectionItems(int start, int end, int sizePerSection, QHeaderView::ResizeMode mode)
3805{
3806 if (end >= sectionItems.size()) {
3807 sectionItems.resize(end + 1);
3808 sectionStartposRecalc = true;
3809 }
3810 SectionItem *sectiondata = sectionItems.data();
3811 for (int i = start; i <= end; ++i) {
3812 length += (sizePerSection - sectiondata[i].size);
3813 sectionStartposRecalc |= (sectiondata[i].size != sizePerSection);
3814 sectiondata[i].size = sizePerSection;
3815 sectiondata[i].resizeMode = mode;
3816 }
3817}
3818
3819void QHeaderViewPrivate::removeSectionsFromSectionItems(int start, int end)
3820{
3821 // remove sections
3822 sectionStartposRecalc |= (end != sectionItems.size() - 1);
3823 int removedlength = 0;
3824 for (int u = start; u <= end; ++u)
3825 removedlength += sectionItems.at(u).size;
3826 length -= removedlength;
3827 sectionItems.remove(start, end - start + 1);
3828}
3829
3830void QHeaderViewPrivate::clear()
3831{
3832 if (state != NoClear) {
3833 length = 0;
3834 countInNoSectionItemsMode = 0;
3835 visualIndices.clear();
3836 logicalIndices.clear();
3837 sectionSelected.clear();
3838 hiddenSectionSize.clear();
3839 sectionItems.clear();
3840 lastSectionLogicalIdx = -1;
3841 invalidateCachedSizeHint();
3842 }
3843}
3844
3845static Qt::SortOrder flipOrder(Qt::SortOrder order)
3846{
3847 switch (order) {
3848 case Qt::AscendingOrder:
3849 return Qt::DescendingOrder;
3850 case Qt::DescendingOrder:
3851 return Qt::AscendingOrder;
3852 };
3853 Q_UNREACHABLE_RETURN(Qt::AscendingOrder);
3854};
3855
3856void QHeaderViewPrivate::flipSortIndicator(int section)
3857{
3858 Q_Q(QHeaderView);
3859 Qt::SortOrder sortOrder;
3860 if (sortIndicatorSection == section) {
3861 if (sortIndicatorClearable) {
3862 const Qt::SortOrder defaultSortOrder = defaultSortOrderForSection(section);
3863 if (sortIndicatorOrder == defaultSortOrder) {
3864 sortOrder = flipOrder(sortIndicatorOrder);
3865 } else {
3866 section = -1;
3867 sortOrder = Qt::AscendingOrder;
3868 }
3869 } else {
3870 sortOrder = flipOrder(sortIndicatorOrder);
3871 }
3872 } else {
3873 sortOrder = defaultSortOrderForSection(section);
3874 }
3875 q->setSortIndicator(section, sortOrder);
3876}
3877
3878Qt::SortOrder QHeaderViewPrivate::defaultSortOrderForSection(int section) const
3879{
3880 const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole);
3881 if (value.canConvert<int>())
3882 return static_cast<Qt::SortOrder>(value.toInt());
3883 return Qt::AscendingOrder;
3884}
3885
3886void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
3887{
3888 Q_Q(QHeaderView);
3889 const int minimumSize = q->minimumSectionSize();
3890 const int oldSize = headerSectionSize(visual);
3891 int delta = newSize - oldSize;
3892
3893 if (delta > 0) { // larger
3894 bool sectionResized = false;
3895
3896 // restore old section sizes
3897 for (int i = firstCascadingSection; i < visual; ++i) {
3898 if (cascadingSectionSize.contains(i)) {
3899 int currentSectionSize = headerSectionSize(i);
3900 int originalSectionSize = cascadingSectionSize.value(i);
3901 if (currentSectionSize < originalSectionSize) {
3902 int newSectionSize = currentSectionSize + delta;
3903 resizeSectionItem(i, currentSectionSize, newSectionSize);
3904 if (newSectionSize >= originalSectionSize && false)
3905 cascadingSectionSize.remove(i); // the section is now restored
3906 sectionResized = true;
3907 break;
3908 }
3909 }
3910
3911 }
3912
3913 // resize the section
3914 if (!sectionResized) {
3915 newSize = qMax(newSize, minimumSize);
3916 if (oldSize != newSize)
3917 resizeSectionItem(visual, oldSize, newSize);
3918 }
3919
3920 // cascade the section size change
3921 for (int i = visual + 1; i < sectionCount(); ++i) {
3922 if (isVisualIndexHidden(i))
3923 continue;
3924 if (!sectionIsCascadable(i))
3925 continue;
3926 int currentSectionSize = headerSectionSize(i);
3927 if (currentSectionSize <= minimumSize)
3928 continue;
3929 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3930 resizeSectionItem(i, currentSectionSize, newSectionSize);
3931 saveCascadingSectionSize(i, currentSectionSize);
3932 delta = delta - (currentSectionSize - newSectionSize);
3933 if (delta <= 0)
3934 break;
3935 }
3936 } else { // smaller
3937 bool sectionResized = false;
3938
3939 // restore old section sizes
3940 for (int i = lastCascadingSection; i > visual; --i) {
3941 if (!cascadingSectionSize.contains(i))
3942 continue;
3943 int currentSectionSize = headerSectionSize(i);
3944 int originalSectionSize = cascadingSectionSize.value(i);
3945 if (currentSectionSize >= originalSectionSize)
3946 continue;
3947 int newSectionSize = currentSectionSize - delta;
3948 resizeSectionItem(i, currentSectionSize, newSectionSize);
3949 if (newSectionSize >= originalSectionSize && false) {
3950 cascadingSectionSize.remove(i); // the section is now restored
3951 }
3952 sectionResized = true;
3953 break;
3954 }
3955
3956 // resize the section
3957 resizeSectionItem(visual, oldSize, qMax(newSize, minimumSize));
3958
3959 // cascade the section size change
3960 if (delta < 0 && newSize < minimumSize) {
3961 for (int i = visual - 1; i >= 0; --i) {
3962 if (isVisualIndexHidden(i))
3963 continue;
3964 if (!sectionIsCascadable(i))
3965 continue;
3966 int sectionSize = headerSectionSize(i);
3967 if (sectionSize <= minimumSize)
3968 continue;
3969 resizeSectionItem(i, sectionSize, qMax(sectionSize + delta, minimumSize));
3970 saveCascadingSectionSize(i, sectionSize);
3971 break;
3972 }
3973 }
3974
3975 // let the next section get the space from the resized section
3976 if (!sectionResized) {
3977 for (int i = visual + 1; i < sectionCount(); ++i) {
3978 if (isVisualIndexHidden(i))
3979 continue;
3980 if (!sectionIsCascadable(i))
3981 continue;
3982 int currentSectionSize = headerSectionSize(i);
3983 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3984 resizeSectionItem(i, currentSectionSize, newSectionSize);
3985 break;
3986 }
3987 }
3988 }
3989
3990 if (hasAutoResizeSections())
3991 doDelayedResizeSections();
3992
3993 viewport->update();
3994}
3995
3996void QHeaderViewPrivate::setDefaultSectionSize(int size)
3997{
3998 Q_Q(QHeaderView);
3999 size = qBound(q->minimumSectionSize(), size, q->maximumSectionSize());
4000 executePostedLayout();
4001 invalidateCachedSizeHint();
4002 defaultSectionSize = size;
4003 customDefaultSectionSize = true;
4004 if (state == QHeaderViewPrivate::ResizeSection)
4005 preventCursorChangeInSetOffset = true;
4006 for (int i = 0; i < sectionItems.size(); ++i) {
4007 QHeaderViewPrivate::SectionItem &section = sectionItems[i];
4008 if (hiddenSectionSize.isEmpty() || !isVisualIndexHidden(i)) { // resize on not hidden.
4009 // Resize to the new default if the current size is the old default,
4010 // or 0. Otherwise don't change.
4011 const int newSize = (section.size == oldDefaultSectionSize || !section.size)
4012 ? size
4013 : section.size;
4014 if (newSize != section.size) {
4015 length += newSize - section.size; //the whole length is changed
4016 const int oldSectionSize = section.sectionSize();
4017 section.size = size;
4018 emit q->sectionResized(logicalIndex(i), oldSectionSize, size);
4019 }
4020 }
4021 }
4022
4023 sectionStartposRecalc = true;
4024 if (hasAutoResizeSections())
4025 doDelayedResizeSections();
4026 viewport->update();
4027
4028 if (noSectionMemoryUsage()) {
4029 length = countInNoSectionItemsMode * size;
4030 QAbstractItemView *parentView = qobject_cast<QAbstractItemView*>(q->parentWidget());
4031 if (parentView)
4032 parentView->viewport()->update();
4033 }
4034}
4035
4036void QHeaderViewPrivate::updateDefaultSectionSizeFromStyle()
4037{
4038 Q_Q(QHeaderView);
4039 if (orientation == Qt::Horizontal) {
4040 defaultSectionSize = q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeHorizontal, nullptr, q);
4041 } else {
4042 defaultSectionSize = qMax(q->minimumSectionSize(),
4043 q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeVertical, nullptr, q));
4044 }
4045}
4046
4047void QHeaderViewPrivate::recalcSectionStartPos() const // linear (but fast)
4048{
4049 int pixelpos = 0;
4050 for (const SectionItem &i : sectionItems) {
4051 i.calculated_startpos = pixelpos; // write into const mutable
4052 pixelpos += i.size;
4053 }
4054 sectionStartposRecalc = false;
4055}
4056
4057void QHeaderViewPrivate::resizeSectionItem(int visualIndex, int oldSize, int newSize)
4058{
4059 Q_Q(QHeaderView);
4060 QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
4061 createSectionItems(visualIndex, visualIndex, newSize, mode);
4062 emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
4063}
4064
4065int QHeaderViewPrivate::headerSectionSize(int visual) const
4066{
4067 if (noSectionMemoryUsage()) {
4068 return defaultSectionSize;
4069 }
4070
4071 if (visual < sectionCount() && visual >= 0)
4072 return sectionItems.at(visual).sectionSize();
4073 return -1;
4074}
4075
4076int QHeaderViewPrivate::headerSectionPosition(int visual) const
4077{
4078 if (noSectionMemoryUsage())
4079 return visual * defaultSectionSize;
4080
4081 if (visual < sectionCount() && visual >= 0) {
4082 if (sectionStartposRecalc)
4083 recalcSectionStartPos();
4084 return sectionItems.at(visual).calculated_startpos;
4085 }
4086 return -1;
4087}
4088
4089int QHeaderViewPrivate::headerVisualIndexAt(int position) const
4090{
4091 if (noSectionMemoryUsage()) {
4092 if (position >= length || position < 0)
4093 return -1;
4094 if (defaultSectionSize > 0)
4095 return position / defaultSectionSize;
4096 return 0;
4097 }
4098
4099 if (sectionStartposRecalc)
4100 recalcSectionStartPos();
4101 int startidx = 0;
4102 int endidx = sectionItems.size() - 1;
4103 while (startidx <= endidx) {
4104 int middle = (endidx + startidx) / 2;
4105 if (sectionItems.at(middle).calculated_startpos > position) {
4106 endidx = middle - 1;
4107 } else {
4108 if (sectionItems.at(middle).calculatedEndPos() <= position)
4109 startidx = middle + 1;
4110 else // we found it.
4111 return middle;
4112 }
4113 }
4114 return -1;
4115}
4116
4117void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
4118{
4119 if (visual < 0)
4120 return;
4121 if (noSectionMemoryUsage())
4122 switchToFlexibleModeWithSectionMemoryUsage();
4123 int size = headerSectionSize(visual);
4124 createSectionItems(visual, visual, size, mode);
4125}
4126
4127QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
4128{
4129 if (visual < 0 || visual >= sectionItems.size())
4130 return globalResizeMode;
4131 return static_cast<QHeaderView::ResizeMode>(sectionItems.at(visual).resizeMode);
4132}
4133
4134void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
4135{
4136 globalResizeMode = mode;
4137 for (int i = 0; i < sectionItems.size(); ++i)
4138 sectionItems[i].resizeMode = mode;
4139}
4140
4141int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
4142{
4143 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) {
4144 return (orientation == Qt::Horizontal
4145 ? view->sizeHintForColumn(logical)
4146 : view->sizeHintForRow(logical));
4147 }
4148 return 0;
4149}
4150
4151int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
4152{
4153 if (!hiddenSectionSize.isEmpty()) {
4154 int adjustedVisualIndex = visualIndex;
4155 int currentVisualIndex = 0;
4156 for (int i = 0; i < sectionItems.size(); ++i) {
4157 if (isVisualIndexHidden(i))
4158 ++adjustedVisualIndex;
4159 else
4160 ++currentVisualIndex;
4161 if (currentVisualIndex >= visualIndex)
4162 break;
4163 }
4164 visualIndex = adjustedVisualIndex;
4165 }
4166 return visualIndex;
4167}
4168
4169void QHeaderViewPrivate::setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode)
4170{
4171 Q_Q(QHeaderView);
4172 if (scrollMode == QAbstractItemView::ScrollPerItem) {
4173 if (scrollBar->maximum() > 0 && scrollBar->value() == scrollBar->maximum())
4174 q->setOffsetToLastSection();
4175 else
4176 q->setOffsetToSectionPosition(scrollBar->value());
4177 } else {
4178 q->setOffset(scrollBar->value());
4179 }
4180}
4181
4182void QHeaderViewPrivate::updateSectionsBeforeAfter(int logical)
4183{
4184 Q_Q(QHeaderView);
4185 const int visual = visualIndex(logical);
4186 int from = logicalIndex(visual > 1 ? visual - 1 : 0);
4187 int to = logicalIndex(visual + 1 >= sectionCount() ? visual : visual + 1);
4188 QRect updateRect;
4189 if (orientation == Qt::Horizontal) {
4190 if (reverse())
4191 std::swap(from, to);
4192 updateRect = QRect(QPoint(q->sectionViewportPosition(from), 0),
4193 QPoint(q->sectionViewportPosition(to) + headerSectionSize(to), viewport->height()));
4194 } else {
4195 updateRect = QRect(QPoint(0, q->sectionViewportPosition(from)),
4196 QPoint(viewport->width(), q->sectionViewportPosition(to) + headerSectionSize(to)));
4197 }
4198 viewport->update(updateRect);
4199}
4200
4201#ifndef QT_NO_DATASTREAM
4202void QHeaderViewPrivate::write(QDataStream &out) const
4203{
4204 out << int(orientation);
4205 out << int(sortIndicatorOrder);
4206 out << sortIndicatorSection;
4207 out << sortIndicatorShown;
4208
4209 out << visualIndices;
4210 out << logicalIndices;
4211
4212 out << sectionsHiddenToBitVector();
4213 out << hiddenSectionSize;
4214
4215 out << length;
4216 out << sectionCount();
4217 out << movableSections;
4218 out << clickableSections;
4219 out << highlightSelected;
4220 out << stretchLastSection;
4221 out << cascadingResizing;
4222 out << stretchSections;
4223 out << contentsSections;
4224 out << defaultSectionSize;
4225 out << minimumSectionSize;
4226
4227 out << int(defaultAlignment);
4228 out << int(globalResizeMode);
4229
4230 if (noSectionMemoryUsage()) {
4231 Q_ASSERT(sectionItems.isEmpty());
4232 out << QList<SectionItem>(); // Avoid storing invalid data in case we encounter a bug somewhere.
4233 }
4234 else {
4235 out << sectionItems;
4236 }
4237
4238 out << resizeContentsPrecision;
4239 out << customDefaultSectionSize;
4240 out << lastSectionSize;
4241 out << int(sortIndicatorClearable);
4242
4243 out << countInNoSectionItemsMode;
4244 out << int(headerMode);
4245}
4246
4247bool QHeaderViewPrivate::read(QDataStream &in)
4248{
4249 Q_Q(QHeaderView);
4250 int orient, order, align, global;
4251 int sortIndicatorSectionIn;
4252 bool sortIndicatorShownIn;
4253 int lengthIn;
4254 QList<int> visualIndicesIn;
4255 QList<int> logicalIndicesIn;
4256 QHash<int, int> hiddenSectionSizeIn;
4257 bool movableSectionsIn;
4258 bool clickableSectionsIn;
4259 bool highlightSelectedIn;
4260 bool stretchLastSectionIn;
4261 bool cascadingResizingIn;
4262 int stretchSectionsIn;
4263 int contentsSectionsIn;
4264 int defaultSectionSizeIn;
4265 int minimumSectionSizeIn;
4266 QList<SectionItem> sectionItemsIn;
4267
4268 in >> orient;
4269 in >> order;
4270
4271 in >> sortIndicatorSectionIn;
4272 in >> sortIndicatorShownIn;
4273
4274 in >> visualIndicesIn;
4275 in >> logicalIndicesIn;
4276
4277 QBitArray sectionHidden;
4278 in >> sectionHidden;
4279 in >> hiddenSectionSizeIn;
4280 in >> lengthIn;
4281
4282 int unusedSectionCount; // For compatibility
4283 in >> unusedSectionCount;
4284
4285 if (in.status() != QDataStream::Ok || lengthIn < 0)
4286 return false;
4287
4288 in >> movableSectionsIn;
4289 in >> clickableSectionsIn;
4290 in >> highlightSelectedIn;
4291 in >> stretchLastSectionIn;
4292 in >> cascadingResizingIn;
4293 in >> stretchSectionsIn;
4294 in >> contentsSectionsIn;
4295 in >> defaultSectionSizeIn;
4296 in >> minimumSectionSizeIn;
4297
4298 in >> align;
4299
4300 in >> global;
4301
4302 // Check parameter consistency
4303 // Global orientation out of bounds?
4304 if (global < 0 || global > QHeaderView::ResizeToContents)
4305 return false;
4306
4307 // Alignment out of bounds?
4308 if (align < 0 || align > Qt::AlignVertical_Mask)
4309 return false;
4310
4311 in >> sectionItemsIn;
4312
4313
4314 // In Qt4 we had a vector of spans where one span could hold information on more sections.
4315 // Now we have an itemvector where one items contains information about one section
4316 // For backward compatibility with Qt4 we do the following
4317 QList<SectionItem> newSectionItems;
4318 for (int u = 0; u < sectionItemsIn.size(); ++u) {
4319 int count = sectionItemsIn.at(u).tmpDataStreamSectionCount;
4320 if (count > 1)
4321 sectionItemsIn[u].size /= count;
4322 for (int n = 0; n < count; ++n)
4323 newSectionItems.append(sectionItemsIn[u]);
4324 }
4325
4326 int sectionItemsLengthTotal = 0;
4327 for (const SectionItem &section : std::as_const(newSectionItems))
4328 sectionItemsLengthTotal += section.size;
4329
4330 if (sectionItemsLengthTotal != lengthIn && newSectionItems.size() > 0)
4331 return false;
4332
4333 // visualIndicesIn/logicalIndicesIn map visual <-> logical section indices for
4334 // each other. Each must either be empty (meaning identity mapping) or have
4335 // exactly one entry per section, and every entry must be a valid index into
4336 // the restored section list. Without this check, a corrupted or malicious
4337 // state blob could make logicalIndex()/visualIndex() index out of bounds.
4338 const auto isValidIndexMapping = [](const QList<int> &mapping, qsizetype sectionCount) {
4339 if (mapping.isEmpty())
4340 return true;
4341 if (mapping.size() != sectionCount)
4342 return false;
4343 QDuplicateTracker<int> seenIndices;
4344 for (int index : mapping) {
4345 if (index < 0 || index >= sectionCount)
4346 return false;
4347 if (seenIndices.hasSeen(index))
4348 return false;
4349 }
4350 return true;
4351 };
4352 const qsizetype newSectionCount = newSectionItems.size();
4353 if (!isValidIndexMapping(visualIndicesIn, newSectionCount)
4354 || !isValidIndexMapping(logicalIndicesIn, newSectionCount)) {
4355 return false;
4356 }
4357
4358 // We don't want to do an actual change in normal mode.
4359 // (Hence we have already set up sections etc)
4360 headerMode = HeaderMode::FlexibleWithSectionMemoryUsage;
4361
4362 orientation = static_cast<Qt::Orientation>(orient);
4363 sortIndicatorOrder = static_cast<Qt::SortOrder>(order);
4364 sortIndicatorSection = sortIndicatorSectionIn;
4365 sortIndicatorShown = sortIndicatorShownIn;
4366 visualIndices = visualIndicesIn;
4367 logicalIndices = logicalIndicesIn;
4368 hiddenSectionSize = hiddenSectionSizeIn;
4369 length = lengthIn;
4370
4371 movableSections = movableSectionsIn;
4372 clickableSections = clickableSectionsIn;
4373 highlightSelected = highlightSelectedIn;
4374 stretchLastSection = stretchLastSectionIn;
4375 cascadingResizing = cascadingResizingIn;
4376 stretchSections = stretchSectionsIn;
4377 contentsSections = contentsSectionsIn;
4378 defaultSectionSize = defaultSectionSizeIn;
4379 minimumSectionSize = minimumSectionSizeIn;
4380
4381 defaultAlignment = Qt::Alignment(align);
4382 globalResizeMode = static_cast<QHeaderView::ResizeMode>(global);
4383
4384 sectionItems = newSectionItems;
4385 setHiddenSectionsFromBitVector(sectionHidden);
4386 recalcSectionStartPos();
4387
4388 int tmpint;
4389 if (in >> tmpint) // we haven't read past end
4390 resizeContentsPrecision = tmpint;
4391
4392 bool tmpbool;
4393 if (in >> tmpbool) { // we haven't read past end
4394 customDefaultSectionSize = tmpbool;
4395 if (!customDefaultSectionSize)
4396 updateDefaultSectionSizeFromStyle();
4397 }
4398
4399 lastSectionSize = -1;
4400 int inLastSectionSize;
4401 if (in >> inLastSectionSize)
4402 lastSectionSize = inLastSectionSize;
4403
4404 lastSectionLogicalIdx = -1;
4405 if (stretchLastSection) {
4406 lastSectionLogicalIdx = q->logicalIndex(lastVisibleVisualIndex());
4407 doDelayedResizeSections();
4408 }
4409
4410 int inSortIndicatorClearable;
4411 if (in >> inSortIndicatorClearable) // we haven't read past end
4412 sortIndicatorClearable = inSortIndicatorClearable;
4413
4414 in >> countInNoSectionItemsMode;
4415 int iHeaderMode;
4416
4417 if (!(in >> iHeaderMode)) {
4418 // On any failure (especially reading past end) we consider mode to be normal by default.
4419 iHeaderMode = static_cast<int>(HeaderMode::FlexibleWithSectionMemoryUsage);
4420 }
4421
4422 const HeaderMode impMode = static_cast<HeaderMode>(iHeaderMode);
4423 if (impMode == HeaderMode::FlexibleWithSectionMemoryUsage || impMode == HeaderMode::InitialNoSectionMemoryUsage) {
4424 headerMode = impMode;
4425 }
4426 else {
4427 // Then it must be from a newer version with a new enum value and we simply
4428 // take the best match.
4429 if (countInNoSectionItemsMode > 0 && sectionItems.isEmpty()) {
4430 headerMode = HeaderMode::InitialNoSectionMemoryUsage;
4431 }
4432 else if (countInNoSectionItemsMode <= 0 && !sectionItems.isEmpty()) {
4433 headerMode = HeaderMode::FlexibleWithSectionMemoryUsage;
4434 }
4435 else {
4436 // We shouldn't end up with both sections and a section count (countInNoSectionItemsMode) > 0.
4437 headerMode = HeaderMode::FlexibleWithSectionMemoryUsage;
4438 return false;
4439 }
4440 }
4441
4442 // Append items from model.
4443 const int currentCount = (orient == Qt::Horizontal ? model->columnCount(root) : model->rowCount(root));
4444 if (sectionItems.size() < currentCount) {
4445
4446 if (noSectionMemoryUsage()) {
4447 countInNoSectionItemsMode = currentCount;
4448 length = defaultSectionSize * countInNoSectionItemsMode;
4449 } else {
4450 // we have sections not in the saved state, give them default settings
4451 if (!visualIndicesIn.isEmpty() && !logicalIndicesIn.isEmpty()) {
4452 for (int i = sectionItems.size(); i < currentCount; ++i) {
4453 visualIndices.append(i);
4454 logicalIndices.append(i);
4455 }
4456 }
4457 const int insertCount = currentCount - sectionItems.size();
4458 const int insertLength = defaultSectionSizeIn * insertCount;
4459 lengthIn += insertLength;
4460 SectionItem section(defaultSectionSizeIn, globalResizeMode);
4461 sectionItems.insert(sectionItems.size(), insertCount, section); // append
4462 }
4463 }
4464 return true;
4465}
4466
4467#endif // QT_NO_DATASTREAM
4468
4469QT_END_NAMESPACE
4470
4471#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