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

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