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

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