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 setResizeMode(), 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(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
361 this, SLOT(sectionsInserted(QModelIndex,int,int)));
362 QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
363 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
364 QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
365 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
366 QObject::disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
367 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
368 QObject::disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
369 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
370 } else {
371 QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
372 this, SLOT(sectionsInserted(QModelIndex,int,int)));
373 QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
374 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
375 QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
376 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
377 QObject::disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
378 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
379 QObject::disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
380 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
381 }
382 QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
383 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
384 QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
385 this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
386 QObject::disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
387 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(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
393 this, SLOT(sectionsInserted(QModelIndex,int,int)));
394 QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
395 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
396 QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
397 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
398 QObject::connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
399 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
400 QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
401 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
402 } else {
403 QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
404 this, SLOT(sectionsInserted(QModelIndex,int,int)));
405 QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
406 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
407 QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
408 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
409 QObject::connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
410 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
411 QObject::connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
412 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
413 }
414 QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
415 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
416 QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
417 this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
418 QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
419 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(isRightToLeft() ? -ndelta : ndelta, 0);
474 else
475 d->viewport->scroll(0, ndelta);
476 if (d->state == QHeaderViewPrivate::ResizeSection && !d->preventCursorChangeInSetOffset) {
477 QPoint cursorPos = QCursor::pos();
478 if (d->orientation == Qt::Horizontal)
479 QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
480 else
481 QCursor::setPos(cursorPos.x(), 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(d->adjustedVisualIndex(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(i))
551 continue;
552 checked++;
553 QSize hint = sectionSizeFromContents(i);
554 d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
555 }
556 // get size hint for the last n sections
557 i = qMax(i, sectionCount - 100 );
558 for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) {
559 if (isSectionHidden(j))
560 continue;
561 checked++;
562 QSize hint = sectionSizeFromContents(j);
563 d->cachedSizeHint = d->cachedSizeHint.expandedTo(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*>(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(logicalIndex, d->orientation, Qt::SizeHintRole);
600 if (value.isValid())
601 size = qvariant_cast<QSize>(value);
602 else
603 size = sectionSizeFromContents(logicalIndex);
604 int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
605 return qBound(minimumSectionSize(), hint, 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(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(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(from);
773 Q_ASSERT(logical != -1);
774 updateSection(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(d->sectionItems, from, from + 1, to);
802
803 d->sectionStartposRecalc = true;
804
805 if (d->hasAutoResizeSections())
806 d->doDelayedResizeSections();
807 d->viewport->update();
808
809 emit sectionMoved(logical, from, to);
810
811 if (stretchLastSection()) {
812 const int lastSectionVisualIdx = visualIndex(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(first);
836 ResizeMode firstMode = d->headerSectionResizeMode(first);
837 int firstLogical = d->logicalIndex(first);
838
839 int secondSize = d->headerSectionSize(second);
840 ResizeMode secondMode = d->headerSectionResizeMode(second);
841 int secondLogical = d->logicalIndex(second);
842
843 if (d->state == QHeaderViewPrivate::ResizeSection)
844 d->preventCursorChangeInSetOffset = true;
845
846 d->createSectionItems(second, second, firstSize, firstMode);
847 d->createSectionItems(first, first, secondSize, 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(first);
859 bool secondHidden = d->isVisualIndexHidden(second);
860 d->setVisualIndexHidden(first, secondHidden);
861 d->setVisualIndexHidden(second, firstHidden);
862 }
863
864 d->viewport->update();
865 emit sectionMoved(firstLogical, first, second);
866 emit sectionMoved(secondLogical, second, first);
867
868 if (stretchLastSection()) {
869 const int lastSectionVisualIdx = visualIndex(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(), resizeMode(), 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(minimumSectionSize(), size, maximumSectionSize());
895
896 if (isSectionHidden(logical)) {
897 d->hiddenSectionSize.insert(logical, size);
898 return;
899 }
900
901 int visual = visualIndex(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(visual, visual, size, d->headerSectionResizeMode(visual));
919
920 if (!updatesEnabled()) {
921 if (d->hasAutoResizeSections())
922 d->doDelayedResizeSections();
923 emit sectionResized(logical, oldSize, size);
924 return;
925 }
926
927 int w = d->viewport->width();
928 int h = d->viewport->height();
929 int pos = sectionViewportPosition(logical);
930 QRect r;
931 if (d->orientation == Qt::Horizontal)
932 if (isRightToLeft())
933 r.setRect(0, 0, pos + size, h);
934 else
935 r.setRect(pos, 0, w - pos, h);
936 else
937 r.setRect(0, pos, w, 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 *>(parentWidget());
952 if (parent && parent->sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents)
953 parent->updateGeometry();
954
955 d->viewport->update(r.normalized());
956 emit sectionResized(logical, oldSize, size);
957}
958
959/*!
960 Resizes the sections according to the given \a mode, ignoring the current
961 resize mode.
962
963 \sa resizeMode(), sectionResized()
964*/
965
966void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
967{
968 Q_D(QHeaderView);
969 d->resizeSections(mode, 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(logicalIndex, 0);
1044 d->hiddenSectionSize.insert(logicalIndex, size);
1045 d->setVisualIndexHidden(visual, true);
1046 if (isHidingLastSection)
1047 d->setNewLastSection(d->lastVisibleVisualIndex());
1048 if (d->hasAutoResizeSections())
1049 d->doDelayedResizeSections();
1050 } else {
1051 int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
1052 d->hiddenSectionSize.remove(logicalIndex);
1053 d->setVisualIndexHidden(visual, false);
1054 resizeSection(logicalIndex, size);
1055
1056 const bool newLastSection = (stretchLastSection() && visual > visualIndex(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(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// ### Qt 6 - remove this obsolete function
1143/*!
1144 \obsolete
1145 \fn void QHeaderView::setMovable(bool movable)
1146
1147 Use setSectionsMovable instead.
1148
1149 \sa setSectionsMovable()
1150*/
1151
1152/*!
1153 \since 5.0
1154
1155 Returns \c true if the header can be moved by the user; otherwise returns
1156 false.
1157
1158 By default, sections are movable in QTreeView (except for the first one),
1159 and not movable in QTableView.
1160
1161 \sa setSectionsMovable()
1162*/
1163
1164bool QHeaderView::sectionsMovable() const
1165{
1166 Q_D(const QHeaderView);
1167 return d->movableSections;
1168}
1169
1170// ### Qt 6 - remove this obsolete function
1171/*!
1172 \obsolete
1173 \fn bool QHeaderView::isMovable() const
1174
1175 Use sectionsMovable instead.
1176
1177 \sa sectionsMovable()
1178*/
1179
1180/*!
1181 \property QHeaderView::firstSectionMovable
1182 \brief Whether the first column can be moved by the user
1183
1184 This property controls whether the first column can be moved by the user.
1185 In a QTreeView, the first column holds the tree structure and is
1186 therefore non-movable by default, even after setSectionsMovable(true).
1187
1188 It can be made movable again, for instance in the case of flat lists
1189 without a tree structure, by calling this method.
1190 In such a scenario, it is recommended to call QTreeView::setRootIsDecorated(false)
1191 as well.
1192
1193 Setting it to true has no effect unless setSectionsMovable(true) is called
1194 as well.
1195
1196 \sa setSectionsMovable()
1197 \since 5.11
1198*/
1199void QHeaderView::setFirstSectionMovable(bool movable)
1200{
1201 Q_D(QHeaderView);
1202 d->allowUserMoveOfSection0 = movable;
1203}
1204
1205bool QHeaderView::isFirstSectionMovable() const
1206{
1207 Q_D(const QHeaderView);
1208 return d->allowUserMoveOfSection0;
1209}
1210
1211/*!
1212 \since 5.0
1213
1214 If \a clickable is true, the header will respond to single clicks.
1215
1216 \sa sectionsClickable(), sectionClicked(), sectionPressed(),
1217 setSortIndicatorShown()
1218*/
1219
1220void QHeaderView::setSectionsClickable(bool clickable)
1221{
1222 Q_D(QHeaderView);
1223 d->clickableSections = clickable;
1224}
1225
1226// ### Qt 6 - remove this obsolete function
1227/*!
1228 \obsolete
1229 \fn void QHeaderView::setClickable(bool clickable)
1230
1231 Use setSectionsClickable instead.
1232
1233 \sa setSectionsClickable()
1234*/
1235
1236/*!
1237 \since 5.0
1238
1239 Returns \c true if the header is clickable; otherwise returns \c false. A
1240 clickable header could be set up to allow the user to change the
1241 representation of the data in the view related to the header.
1242
1243 \sa setSectionsClickable()
1244*/
1245
1246bool QHeaderView::sectionsClickable() const
1247{
1248 Q_D(const QHeaderView);
1249 return d->clickableSections;
1250}
1251
1252// ### Qt 6 - remove this obsolete function
1253/*!
1254 \obsolete
1255 \fn bool QHeaderView::isClickable() const
1256
1257 Use sectionsClickable instead.
1258
1259 \sa sectionsClickable()
1260*/
1261
1262void QHeaderView::setHighlightSections(bool highlight)
1263{
1264 Q_D(QHeaderView);
1265 d->highlightSelected = highlight;
1266}
1267
1268bool QHeaderView::highlightSections() const
1269{
1270 Q_D(const QHeaderView);
1271 return d->highlightSelected;
1272}
1273
1274/*!
1275 \since 5.0
1276
1277 Sets the constraints on how the header can be resized to those described
1278 by the given \a mode.
1279
1280 \sa resizeMode(), length(), sectionResized()
1281*/
1282
1283void QHeaderView::setSectionResizeMode(ResizeMode mode)
1284{
1285 Q_D(QHeaderView);
1286 initializeSections();
1287 d->stretchSections = (mode == Stretch ? count() : 0);
1288 d->contentsSections = (mode == ResizeToContents ? count() : 0);
1289 d->setGlobalHeaderResizeMode(mode);
1290 if (d->hasAutoResizeSections())
1291 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1292}
1293
1294/*!
1295 \since 5.0
1296
1297 Sets the constraints on how the section specified by \a logicalIndex in
1298 the header can be resized to those described by the given \a mode. The logical
1299 index should exist at the time this function is called.
1300
1301 \note This setting will be ignored for the last section if the stretchLastSection
1302 property is set to true. This is the default for the horizontal headers provided
1303 by QTreeView.
1304
1305 \sa setStretchLastSection(), resizeContentsPrecision()
1306*/
1307
1308void QHeaderView::setSectionResizeMode(int logicalIndex, ResizeMode mode)
1309{
1310 Q_D(QHeaderView);
1311 int visual = visualIndex(logicalIndex);
1312 Q_ASSERT(visual != -1);
1313
1314 ResizeMode old = d->headerSectionResizeMode(visual);
1315 d->setHeaderSectionResizeMode(visual, mode);
1316
1317 if (mode == Stretch && old != Stretch)
1318 ++d->stretchSections;
1319 else if (mode == ResizeToContents && old != ResizeToContents)
1320 ++d->contentsSections;
1321 else if (mode != Stretch && old == Stretch)
1322 --d->stretchSections;
1323 else if (mode != ResizeToContents && old == ResizeToContents)
1324 --d->contentsSections;
1325
1326 if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
1327 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1328}
1329
1330// ### Qt 6 - remove this obsolete function
1331/*!
1332 \overload
1333 \obsolete
1334 \fn void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
1335
1336 Use setSectionResizeMode instead.
1337
1338 \sa setSectionResizeMode()
1339*/
1340
1341/*!
1342 \obsolete
1343 \fn void QHeaderView::setResizeMode(ResizeMode mode)
1344
1345 Use setSectionResizeMode instead.
1346
1347 \sa setSectionResizeMode()
1348*/
1349
1350/*!
1351 \since 5.0
1352
1353 Returns the resize mode that applies to the section specified by the given
1354 \a logicalIndex.
1355
1356 \sa setSectionResizeMode()
1357*/
1358
1359QHeaderView::ResizeMode QHeaderView::sectionResizeMode(int logicalIndex) const
1360{
1361 Q_D(const QHeaderView);
1362 int visual = visualIndex(logicalIndex);
1363 if (visual == -1)
1364 return Fixed; //the default value
1365 return d->headerSectionResizeMode(visual);
1366}
1367
1368/*!
1369 \since 5.2
1370 Sets how precise QHeaderView should calculate the size when ResizeToContents is used.
1371 A low value will provide a less accurate but fast auto resize while a higher
1372 value will provide a more accurate resize that however can be slow.
1373
1374 The number \a precision specifies how many sections that should be consider
1375 when calculating the preferred size.
1376
1377 The default value is 1000 meaning that a horizontal column with auto-resize will look
1378 at maximum 1000 rows on calculating when doing an auto resize.
1379
1380 Special value 0 means that it will look at only the visible area.
1381 Special value -1 will imply looking at all elements.
1382
1383 This value is used in QTableView::sizeHintForColumn(), QTableView::sizeHintForRow()
1384 and QTreeView::sizeHintForColumn(). Reimplementing these functions can make this
1385 function not having an effect.
1386
1387 \sa resizeContentsPrecision(), setSectionResizeMode(), resizeSections(), QTableView::sizeHintForColumn(), QTableView::sizeHintForRow(), QTreeView::sizeHintForColumn()
1388*/
1389
1390void QHeaderView::setResizeContentsPrecision(int precision)
1391{
1392 Q_D(QHeaderView);
1393 d->resizeContentsPrecision = precision;
1394}
1395
1396/*!
1397 \since 5.2
1398 Returns how precise QHeaderView will calculate on ResizeToContents.
1399
1400 \sa setResizeContentsPrecision(), setSectionResizeMode()
1401
1402*/
1403
1404int QHeaderView::resizeContentsPrecision() const
1405{
1406 Q_D(const QHeaderView);
1407 return d->resizeContentsPrecision;
1408}
1409
1410// ### Qt 6 - remove this obsolete function
1411/*!
1412 \obsolete
1413 \fn QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
1414
1415 Use sectionResizeMode instead.
1416
1417 \sa sectionResizeMode()
1418*/
1419
1420/*!
1421 \since 4.1
1422
1423 Returns the number of sections that are set to resize mode stretch. In
1424 views, this can be used to see if the headerview needs to resize the
1425 sections when the view's geometry changes.
1426
1427 \sa stretchLastSection, resizeMode()
1428*/
1429
1430int QHeaderView::stretchSectionCount() const
1431{
1432 Q_D(const QHeaderView);
1433 return d->stretchSections;
1434}
1435
1436/*!
1437 \property QHeaderView::showSortIndicator
1438 \brief whether the sort indicator is shown
1439
1440 By default, this property is \c false.
1441
1442 \sa setSectionsClickable()
1443*/
1444
1445void QHeaderView::setSortIndicatorShown(bool show)
1446{
1447 Q_D(QHeaderView);
1448 if (d->sortIndicatorShown == show)
1449 return;
1450
1451 d->sortIndicatorShown = show;
1452
1453 if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
1454 return;
1455
1456 if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents)
1457 resizeSections();
1458
1459 d->viewport->update();
1460}
1461
1462bool QHeaderView::isSortIndicatorShown() const
1463{
1464 Q_D(const QHeaderView);
1465 return d->sortIndicatorShown;
1466}
1467
1468/*!
1469 Sets the sort indicator for the section specified by the given
1470 \a logicalIndex in the direction specified by \a order, and removes the
1471 sort indicator from any other section that was showing it.
1472
1473 \a logicalIndex may be -1, in which case no sort indicator will be shown
1474 and the model will return to its natural, unsorted order. Note that not
1475 all models support this and may even crash in this case.
1476
1477 \sa sortIndicatorSection(), sortIndicatorOrder()
1478*/
1479
1480void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
1481{
1482 Q_D(QHeaderView);
1483
1484 // This is so that people can set the position of the sort indicator before the fill the model
1485 int old = d->sortIndicatorSection;
1486 if (old == logicalIndex && order == d->sortIndicatorOrder)
1487 return;
1488 d->sortIndicatorSection = logicalIndex;
1489 d->sortIndicatorOrder = order;
1490
1491 if (logicalIndex >= d->sectionCount()) {
1492 emit sortIndicatorChanged(logicalIndex, order);
1493 return; // nothing to do
1494 }
1495
1496 if (old != logicalIndex
1497 && ((logicalIndex >= 0 && sectionResizeMode(logicalIndex) == ResizeToContents)
1498 || old >= d->sectionCount() || (old >= 0 && sectionResizeMode(old) == ResizeToContents))) {
1499 resizeSections();
1500 d->viewport->update();
1501 } else {
1502 if (old >= 0 && old != logicalIndex)
1503 updateSection(old);
1504 if (logicalIndex >= 0)
1505 updateSection(logicalIndex);
1506 }
1507
1508 emit sortIndicatorChanged(logicalIndex, order);
1509}
1510
1511/*!
1512 Returns the logical index of the section that has a sort indicator.
1513 By default this is section 0.
1514
1515 \sa setSortIndicator(), sortIndicatorOrder(), setSortIndicatorShown()
1516*/
1517
1518int QHeaderView::sortIndicatorSection() const
1519{
1520 Q_D(const QHeaderView);
1521 return d->sortIndicatorSection;
1522}
1523
1524/*!
1525 Returns the order for the sort indicator. If no section has a sort
1526 indicator the return value of this function is undefined.
1527
1528 \sa setSortIndicator(), sortIndicatorSection()
1529*/
1530
1531Qt::SortOrder QHeaderView::sortIndicatorOrder() const
1532{
1533 Q_D(const QHeaderView);
1534 return d->sortIndicatorOrder;
1535}
1536
1537/*!
1538 \property QHeaderView::stretchLastSection
1539 \brief whether the last visible section in the header takes up all the
1540 available space
1541
1542 The default value is false.
1543
1544 \note The horizontal headers provided by QTreeView are configured with this
1545 property set to true, ensuring that the view does not waste any of the
1546 space assigned to it for its header. If this value is set to true, this
1547 property will override the resize mode set on the last section in the
1548 header.
1549
1550 \sa setSectionResizeMode()
1551*/
1552bool QHeaderView::stretchLastSection() const
1553{
1554 Q_D(const QHeaderView);
1555 return d->stretchLastSection;
1556}
1557
1558void QHeaderView::setStretchLastSection(bool stretch)
1559{
1560 Q_D(QHeaderView);
1561 if (d->stretchLastSection == stretch)
1562 return;
1563 d->stretchLastSection = stretch;
1564 if (d->state != QHeaderViewPrivate::NoState)
1565 return;
1566 if (stretch) {
1567 d->setNewLastSection(d->lastVisibleVisualIndex());
1568 resizeSections();
1569 } else {
1570 d->restoreSizeOnPrevLastSection();
1571 }
1572}
1573
1574/*!
1575 \since 4.2
1576 \property QHeaderView::cascadingSectionResizes
1577 \brief whether interactive resizing will be cascaded to the following
1578 sections once the section being resized by the user has reached its
1579 minimum size
1580
1581 This property only affects sections that have \l Interactive as their
1582 resize mode.
1583
1584 The default value is false.
1585
1586 \sa setSectionResizeMode()
1587*/
1588bool QHeaderView::cascadingSectionResizes() const
1589{
1590 Q_D(const QHeaderView);
1591 return d->cascadingResizing;
1592}
1593
1594void QHeaderView::setCascadingSectionResizes(bool enable)
1595{
1596 Q_D(QHeaderView);
1597 d->cascadingResizing = enable;
1598}
1599
1600/*!
1601 \property QHeaderView::defaultSectionSize
1602 \brief the default size of the header sections before resizing.
1603
1604 This property only affects sections that have \l Interactive or \l Fixed
1605 as their resize mode.
1606
1607 By default, the value of this property is style dependent.
1608 Thus, when the style changes, this property updates from it.
1609 Calling setDefaultSectionSize() stops the updates, calling
1610 resetDefaultSectionSize() will restore default behavior.
1611
1612 \sa setSectionResizeMode(), minimumSectionSize
1613*/
1614int QHeaderView::defaultSectionSize() const
1615{
1616 Q_D(const QHeaderView);
1617 return d->defaultSectionSize;
1618}
1619
1620void QHeaderView::setDefaultSectionSize(int size)
1621{
1622 Q_D(QHeaderView);
1623 if (size < 0 || size > maxSizeSection)
1624 return;
1625 d->setDefaultSectionSize(size);
1626}
1627
1628void QHeaderView::resetDefaultSectionSize()
1629{
1630 Q_D(QHeaderView);
1631 if (d->customDefaultSectionSize) {
1632 d->updateDefaultSectionSizeFromStyle();
1633 d->customDefaultSectionSize = false;
1634 }
1635}
1636
1637/*!
1638 \since 4.2
1639 \property QHeaderView::minimumSectionSize
1640 \brief the minimum size of the header sections.
1641
1642 The minimum section size is the smallest section size allowed. If the
1643 minimum section size is set to -1, QHeaderView will use the maximum of
1644 the \l{QApplication::globalStrut()}{global strut} or the
1645 \l{fontMetrics()}{font metrics} size.
1646
1647 This property is honored by all \l{ResizeMode}{resize modes}.
1648
1649 \sa setSectionResizeMode(), defaultSectionSize
1650*/
1651int QHeaderView::minimumSectionSize() const
1652{
1653 Q_D(const QHeaderView);
1654 if (d->minimumSectionSize == -1) {
1655 QSize strut = QApplication::globalStrut();
1656 int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
1657 if (d->orientation == Qt::Horizontal)
1658 return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
1659 return qMax(strut.height(), (fontMetrics().height() + margin));
1660 }
1661 return d->minimumSectionSize;
1662}
1663
1664void QHeaderView::setMinimumSectionSize(int size)
1665{
1666 Q_D(QHeaderView);
1667 if (size < -1 || size > maxSizeSection)
1668 return;
1669 // larger new min size - check current section sizes
1670 const bool needSizeCheck = size > d->minimumSectionSize;
1671 d->minimumSectionSize = size;
1672 if (d->minimumSectionSize > maximumSectionSize())
1673 setMaximumSectionSize(size);
1674
1675 if (needSizeCheck) {
1676 if (d->hasAutoResizeSections()) {
1677 d->doDelayedResizeSections();
1678 } else {
1679 for (int visual = 0; visual < d->sectionCount(); ++visual) {
1680 if (d->isVisualIndexHidden(visual))
1681 continue;
1682 if (d->headerSectionSize(visual) < d->minimumSectionSize)
1683 resizeSection(logicalIndex(visual), size);
1684 }
1685 }
1686 }
1687
1688}
1689
1690/*!
1691 \since 5.2
1692 \property QHeaderView::maximumSectionSize
1693 \brief the maximum size of the header sections.
1694
1695 The maximum section size is the largest section size allowed.
1696 The default value for this property is 1048575, which is also the largest
1697 possible size for a section. Setting maximum to -1 will reset the value to
1698 the largest section size.
1699
1700 With exception of stretch this property is honored by all \l{ResizeMode}{resize modes}
1701
1702 \sa setSectionResizeMode(), defaultSectionSize
1703*/
1704int QHeaderView::maximumSectionSize() const
1705{
1706 Q_D(const QHeaderView);
1707 if (d->maximumSectionSize == -1)
1708 return maxSizeSection;
1709 return d->maximumSectionSize;
1710}
1711
1712void QHeaderView::setMaximumSectionSize(int size)
1713{
1714 Q_D(QHeaderView);
1715 if (size == -1) {
1716 d->maximumSectionSize = maxSizeSection;
1717 return;
1718 }
1719 if (size < 0 || size > maxSizeSection)
1720 return;
1721 if (minimumSectionSize() > size)
1722 d->minimumSectionSize = size;
1723
1724 // smaller new max size - check current section sizes
1725 const bool needSizeCheck = size < d->maximumSectionSize;
1726 d->maximumSectionSize = size;
1727
1728 if (needSizeCheck) {
1729 if (d->hasAutoResizeSections()) {
1730 d->doDelayedResizeSections();
1731 } else {
1732 for (int visual = 0; visual < d->sectionCount(); ++visual) {
1733 if (d->isVisualIndexHidden(visual))
1734 continue;
1735 if (d->headerSectionSize(visual) > d->maximumSectionSize)
1736 resizeSection(logicalIndex(visual), size);
1737 }
1738 }
1739 }
1740}
1741
1742
1743/*!
1744 \since 4.1
1745 \property QHeaderView::defaultAlignment
1746 \brief the default alignment of the text in each header section
1747*/
1748
1749Qt::Alignment QHeaderView::defaultAlignment() const
1750{
1751 Q_D(const QHeaderView);
1752 return d->defaultAlignment;
1753}
1754
1755void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
1756{
1757 Q_D(QHeaderView);
1758 if (d->defaultAlignment == alignment)
1759 return;
1760
1761 d->defaultAlignment = alignment;
1762 d->viewport->update();
1763}
1764
1765/*!
1766 \internal
1767*/
1768void QHeaderView::doItemsLayout()
1769{
1770 initializeSections();
1771 QAbstractItemView::doItemsLayout();
1772}
1773
1774/*!
1775 Returns \c true if sections in the header has been moved; otherwise returns
1776 false;
1777
1778 \sa moveSection()
1779*/
1780bool QHeaderView::sectionsMoved() const
1781{
1782 Q_D(const QHeaderView);
1783 return !d->visualIndices.isEmpty();
1784}
1785
1786/*!
1787 \since 4.1
1788
1789 Returns \c true if sections in the header has been hidden; otherwise returns
1790 false;
1791
1792 \sa setSectionHidden()
1793*/
1794bool QHeaderView::sectionsHidden() const
1795{
1796 Q_D(const QHeaderView);
1797 return !d->hiddenSectionSize.isEmpty();
1798}
1799
1800#ifndef QT_NO_DATASTREAM
1801/*!
1802 \since 4.3
1803
1804 Saves the current state of this header view.
1805
1806 To restore the saved state, pass the return value to restoreState().
1807
1808 \sa restoreState()
1809*/
1810QByteArray QHeaderView::saveState() const
1811{
1812 Q_D(const QHeaderView);
1813 QByteArray data;
1814 QDataStream stream(&data, QIODevice::WriteOnly);
1815 stream << QHeaderViewPrivate::VersionMarker;
1816 stream << 0; // current version is 0
1817 d->write(stream);
1818 return data;
1819}
1820
1821/*!
1822 \since 4.3
1823 Restores the \a state of this header view.
1824 This function returns \c true if the state was restored; otherwise returns
1825 false.
1826
1827 \sa saveState()
1828*/
1829bool QHeaderView::restoreState(const QByteArray &state)
1830{
1831 Q_D(QHeaderView);
1832 if (state.isEmpty())
1833 return false;
1834 QByteArray data = state;
1835 QDataStream stream(&data, QIODevice::ReadOnly);
1836 int marker;
1837 int ver;
1838 stream >> marker;
1839 stream >> ver;
1840 if (stream.status() != QDataStream::Ok
1841 || marker != QHeaderViewPrivate::VersionMarker
1842 || ver != 0) // current version is 0
1843 return false;
1844
1845 if (d->read(stream)) {
1846 emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
1847 d->viewport->update();
1848 return true;
1849 }
1850 return false;
1851}
1852#endif // QT_NO_DATASTREAM
1853
1854/*!
1855 \reimp
1856*/
1857void QHeaderView::reset()
1858{
1859 Q_D(QHeaderView);
1860 QAbstractItemView::reset();
1861 // it would be correct to call clear, but some apps rely
1862 // on the header keeping the sections, even after calling reset
1863 //d->clear();
1864 initializeSections();
1865 d->invalidateCachedSizeHint();
1866}
1867
1868/*!
1869 Updates the changed header sections with the given \a orientation, from
1870 \a logicalFirst to \a logicalLast inclusive.
1871*/
1872void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
1873{
1874 Q_D(QHeaderView);
1875 if (d->orientation != orientation)
1876 return;
1877
1878 if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
1879 return;
1880
1881 d->invalidateCachedSizeHint();
1882
1883 int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
1884
1885 for (int section = logicalFirst; section <= logicalLast; ++section) {
1886 const int visual = visualIndex(section);
1887 firstVisualIndex = qMin(firstVisualIndex, visual);
1888 lastVisualIndex = qMax(lastVisualIndex, visual);
1889 }
1890
1891 d->executePostedResize();
1892 const int first = d->headerSectionPosition(firstVisualIndex),
1893 last = d->headerSectionPosition(lastVisualIndex)
1894 + d->headerSectionSize(lastVisualIndex);
1895
1896 if (orientation == Qt::Horizontal) {
1897 d->viewport->update(first, 0, last - first, d->viewport->height());
1898 } else {
1899 d->viewport->update(0, first, d->viewport->width(), last - first);
1900 }
1901}
1902
1903/*!
1904 \internal
1905 \since 4.2
1906
1907 Updates the section specified by the given \a logicalIndex.
1908*/
1909
1910void QHeaderView::updateSection(int logicalIndex)
1911{
1912 Q_D(QHeaderView);
1913 if (d->orientation == Qt::Horizontal)
1914 d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
1915 0, sectionSize(logicalIndex), d->viewport->height()));
1916 else
1917 d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
1918 d->viewport->width(), sectionSize(logicalIndex)));
1919}
1920
1921/*!
1922 Resizes the sections according to their size hints. Normally, you do not
1923 have to call this function.
1924*/
1925
1926void QHeaderView::resizeSections()
1927{
1928 Q_D(QHeaderView);
1929 if (d->hasAutoResizeSections())
1930 d->resizeSections(Interactive, false); // no global resize mode
1931}
1932
1933/*!
1934 This slot is called when sections are inserted into the \a parent.
1935 \a logicalFirst and \a logicalLast indices signify where the new sections
1936 were inserted.
1937
1938 If only one section is inserted, \a logicalFirst and \a logicalLast will
1939 be the same.
1940*/
1941
1942void QHeaderView::sectionsInserted(const QModelIndex &parent,
1943 int logicalFirst, int logicalLast)
1944{
1945 Q_D(QHeaderView);
1946 if (parent != d->root)
1947 return; // we only handle changes in the root level
1948 int oldCount = d->sectionCount();
1949
1950 d->invalidateCachedSizeHint();
1951
1952 if (d->state == QHeaderViewPrivate::ResizeSection)
1953 d->preventCursorChangeInSetOffset = true;
1954
1955 // add the new sections
1956 int insertAt = logicalFirst;
1957 int insertCount = logicalLast - logicalFirst + 1;
1958
1959 bool lastSectionActualChange = false;
1960 if (stretchLastSection()) {
1961
1962 int visualIndexForStretch = d->lastSectionLogicalIdx;
1963 if (d->lastSectionLogicalIdx >= 0 && d->lastSectionLogicalIdx < d->visualIndices.size())
1964 visualIndexForStretch = d->visualIndices[d->lastSectionLogicalIdx]; // We cannot call visualIndex since it executes executePostedLayout()
1965 // and it is likely to bypass initializeSections() and we may end up here again. Doing the insert twice.
1966
1967 if (d->lastSectionLogicalIdx < 0 || insertAt >= visualIndexForStretch)
1968 lastSectionActualChange = true;
1969
1970 if (d->lastSectionLogicalIdx >= logicalFirst)
1971 d->lastSectionLogicalIdx += insertCount; // We do not want to emit resize before we have fixed the count
1972 }
1973
1974 QHeaderViewPrivate::SectionItem section(d->defaultSectionSize, d->globalResizeMode);
1975 d->sectionStartposRecalc = true;
1976
1977 if (d->sectionItems.isEmpty() || insertAt >= d->sectionItems.count()) {
1978 int insertLength = d->defaultSectionSize * insertCount;
1979 d->length += insertLength;
1980 d->sectionItems.insert(d->sectionItems.count(), insertCount, section); // append
1981 } else {
1982 // separate them out into their own sections
1983 int insertLength = d->defaultSectionSize * insertCount;
1984 d->length += insertLength;
1985 d->sectionItems.insert(insertAt, insertCount, section);
1986 }
1987
1988 // update sorting column
1989 if (d->sortIndicatorSection >= logicalFirst)
1990 d->sortIndicatorSection += insertCount;
1991
1992 // update resize mode section counts
1993 if (d->globalResizeMode == Stretch)
1994 d->stretchSections = d->sectionCount();
1995 else if (d->globalResizeMode == ResizeToContents)
1996 d->contentsSections = d->sectionCount();
1997
1998 // clear selection cache
1999 d->sectionSelected.clear();
2000
2001 // update mapping
2002 if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
2003 Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
2004 int mappingCount = d->visualIndices.count();
2005 for (int i = 0; i < mappingCount; ++i) {
2006 if (d->visualIndices.at(i) >= logicalFirst)
2007 d->visualIndices[i] += insertCount;
2008 if (d->logicalIndices.at(i) >= logicalFirst)
2009 d->logicalIndices[i] += insertCount;
2010 }
2011 for (int j = logicalFirst; j <= logicalLast; ++j) {
2012 d->visualIndices.insert(j, j);
2013 d->logicalIndices.insert(j, j);
2014 }
2015 }
2016
2017 // insert sections into hiddenSectionSize
2018 QHash<int, int> newHiddenSectionSize; // from logical index to section size
2019 for (QHash<int, int>::const_iterator it = d->hiddenSectionSize.cbegin(),
2020 end = d->hiddenSectionSize.cend(); it != end; ++it) {
2021 const int oldIndex = it.key();
2022 const int newIndex = (oldIndex < logicalFirst) ? oldIndex : oldIndex + insertCount;
2023 newHiddenSectionSize[newIndex] = it.value();
2024 }
2025 d->hiddenSectionSize.swap(newHiddenSectionSize);
2026
2027 d->doDelayedResizeSections();
2028 emit sectionCountChanged(oldCount, count());
2029
2030 if (lastSectionActualChange)
2031 d->maybeRestorePrevLastSectionAndStretchLast();
2032
2033 // if the new sections were not updated by resizing, we need to update now
2034 if (!d->hasAutoResizeSections())
2035 d->viewport->update();
2036}
2037
2038/*!
2039 This slot is called when sections are removed from the \a parent.
2040 \a logicalFirst and \a logicalLast signify where the sections were removed.
2041
2042 If only one section is removed, \a logicalFirst and \a logicalLast will
2043 be the same.
2044*/
2045
2046void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
2047 int logicalFirst, int logicalLast)
2048{
2049 Q_UNUSED(parent);
2050 Q_UNUSED(logicalFirst);
2051 Q_UNUSED(logicalLast);
2052}
2053
2054void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
2055{
2056 Q_Q(QHeaderView);
2057 const int changeCount = logicalLast - logicalFirst + 1;
2058
2059 // remove sections from hiddenSectionSize
2060 QHash<int, int> newHiddenSectionSize; // from logical index to section size
2061 for (int i = 0; i < logicalFirst; ++i)
2062 if (q->isSectionHidden(i))
2063 newHiddenSectionSize[i] = hiddenSectionSize[i];
2064 for (int j = logicalLast + 1; j < sectionCount(); ++j)
2065 if (q->isSectionHidden(j))
2066 newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
2067 hiddenSectionSize = newHiddenSectionSize;
2068}
2069
2070void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
2071 int logicalFirst, int logicalLast)
2072{
2073 Q_Q(QHeaderView);
2074 if (parent != root)
2075 return; // we only handle changes in the root level
2076 if (qMin(logicalFirst, logicalLast) < 0
2077 || qMax(logicalLast, logicalFirst) >= sectionCount())
2078 return;
2079 int oldCount = q->count();
2080 int changeCount = logicalLast - logicalFirst + 1;
2081
2082 if (state == QHeaderViewPrivate::ResizeSection)
2083 preventCursorChangeInSetOffset = true;
2084
2085 updateHiddenSections(logicalFirst, logicalLast);
2086
2087 if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
2088 //Q_ASSERT(headerSectionCount() == sectionCount);
2089 removeSectionsFromSectionItems(logicalFirst, logicalLast);
2090 } else {
2091 if (logicalFirst == logicalLast) { // Remove just one index.
2092 int l = logicalFirst;
2093 int visual = visualIndices.at(l);
2094 Q_ASSERT(sectionCount() == logicalIndices.count());
2095 for (int v = 0; v < sectionCount(); ++v) {
2096 if (v > visual) {
2097 int logical = logicalIndices.at(v);
2098 --(visualIndices[logical]);
2099 }
2100 if (logicalIndex(v) > l) // no need to move the positions before l
2101 --(logicalIndices[v]);
2102 }
2103 logicalIndices.remove(visual);
2104 visualIndices.remove(l);
2105 //Q_ASSERT(headerSectionCount() == sectionCount);
2106 removeSectionsFromSectionItems(visual, visual);
2107 } else {
2108 sectionStartposRecalc = true; // We will need to recalc positions after removing items
2109 for (int u = 0; u < sectionItems.count(); ++u) // Store section info
2110 sectionItems.at(u).tmpLogIdx = logicalIndices.at(u);
2111 for (int v = sectionItems.count() - 1; v >= 0; --v) { // Remove the sections
2112 if (logicalFirst <= sectionItems.at(v).tmpLogIdx && sectionItems.at(v).tmpLogIdx <= logicalLast)
2113 removeSectionsFromSectionItems(v, v);
2114 }
2115 visualIndices.resize(sectionItems.count());
2116 logicalIndices.resize(sectionItems.count());
2117 int* visual_data = visualIndices.data();
2118 int* logical_data = logicalIndices.data();
2119 for (int w = 0; w < sectionItems.count(); ++w) { // Restore visual and logical indexes
2120 int logindex = sectionItems.at(w).tmpLogIdx;
2121 if (logindex > logicalFirst)
2122 logindex -= changeCount;
2123 visual_data[logindex] = w;
2124 logical_data[w] = logindex;
2125 }
2126 }
2127 // ### handle sectionSelection (sectionHidden is handled by updateHiddenSections)
2128 }
2129
2130 // update sorting column
2131 if (sortIndicatorSection >= logicalFirst) {
2132 if (sortIndicatorSection <= logicalLast)
2133 sortIndicatorSection = -1;
2134 else
2135 sortIndicatorSection -= changeCount;
2136 }
2137
2138 // if we only have the last section (the "end" position) left, the header is empty
2139 if (sectionCount() <= 0)
2140 clear();
2141 invalidateCachedSizeHint();
2142 emit q->sectionCountChanged(oldCount, q->count());
2143
2144 if (q->stretchLastSection()) {
2145 const bool lastSectionRemoved = lastSectionLogicalIdx >= logicalFirst && lastSectionLogicalIdx <= logicalLast;
2146 if (lastSectionRemoved)
2147 setNewLastSection(lastVisibleVisualIndex());
2148 else
2149 lastSectionLogicalIdx = logicalIndex(lastVisibleVisualIndex()); // Just update the last log index.
2150 doDelayedResizeSections();
2151 }
2152
2153 viewport->update();
2154}
2155
2156void QHeaderViewPrivate::_q_sectionsAboutToBeMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2157{
2158 if (sourceParent != root || destinationParent != root)
2159 return; // we only handle changes in the root level
2160 Q_UNUSED(logicalStart);
2161 Q_UNUSED(logicalEnd);
2162 Q_UNUSED(logicalDestination);
2163 _q_sectionsAboutToBeChanged();
2164}
2165
2166void QHeaderViewPrivate::_q_sectionsMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2167{
2168 if (sourceParent != root || destinationParent != root)
2169 return; // we only handle changes in the root level
2170 Q_UNUSED(logicalStart);
2171 Q_UNUSED(logicalEnd);
2172 Q_UNUSED(logicalDestination);
2173 _q_sectionsChanged();
2174}
2175
2176void QHeaderViewPrivate::_q_sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &,
2177 QAbstractItemModel::LayoutChangeHint hint)
2178{
2179 if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2180 (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2181 return;
2182
2183 //if there is no row/column we can't have mapping for columns
2184 //because no QModelIndex in the model would be valid
2185 // ### this is far from being bullet-proof and we would need a real system to
2186 // ### map columns or rows persistently
2187 if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
2188 || model->columnCount(root) == 0)
2189 return;
2190
2191 layoutChangePersistentSections.clear();
2192 layoutChangePersistentSections.reserve(std::min(10, sectionItems.count()));
2193 // after layoutChanged another section can be last stretched section
2194 if (stretchLastSection && lastSectionLogicalIdx >= 0 && lastSectionLogicalIdx < sectionItems.count()) {
2195 const int visual = visualIndex(lastSectionLogicalIdx);
2196 if (visual >= 0 && visual < sectionItems.size()) {
2197 auto &itemRef = sectionItems[visual];
2198 if (itemRef.size != lastSectionSize) {
2199 length += lastSectionSize - itemRef.size;
2200 itemRef.size = lastSectionSize;
2201 }
2202 }
2203 }
2204 for (int i = 0; i < sectionItems.size(); ++i) {
2205 auto s = sectionItems.at(i);
2206 // only add if the section is not default and not visually moved
2207 if (s.size == defaultSectionSize && !s.isHidden && s.resizeMode == globalResizeMode)
2208 continue;
2209
2210 const int logical = logicalIndex(i);
2211 if (s.isHidden)
2212 s.size = hiddenSectionSize.value(logical);
2213
2214 // ### note that we are using column or row 0
2215 layoutChangePersistentSections.append({orientation == Qt::Horizontal
2216 ? model->index(0, logical, root)
2217 : model->index(logical, 0, root),
2218 s});
2219 }
2220}
2221
2222void QHeaderViewPrivate::_q_sectionsChanged(const QList<QPersistentModelIndex> &,
2223 QAbstractItemModel::LayoutChangeHint hint)
2224{
2225 if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2226 (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2227 return;
2228
2229 Q_Q(QHeaderView);
2230 viewport->update();
2231
2232 const auto oldPersistentSections = layoutChangePersistentSections;
2233 layoutChangePersistentSections.clear();
2234
2235 const int newCount = modelSectionCount();
2236 const int oldCount = sectionItems.size();
2237 if (newCount == 0) {
2238 clear();
2239 if (oldCount != 0)
2240 emit q->sectionCountChanged(oldCount, 0);
2241 return;
2242 }
2243
2244 bool hasPersistantIndexes = false;
2245 for (const auto &item : oldPersistentSections) {
2246 if (item.index.isValid()) {
2247 hasPersistantIndexes = true;
2248 break;
2249 }
2250 }
2251
2252 // Though far from perfect we here try to retain earlier/existing behavior
2253 // ### See QHeaderViewPrivate::_q_layoutAboutToBeChanged()
2254 // When we don't have valid hasPersistantIndexes it can be due to
2255 // - all sections are default sections
2256 // - the row/column 0 which is used for persistent indexes is gone
2257 // - all non-default sections were removed
2258 // case one is trivial, in case two we assume nothing else changed (it's the best
2259 // guess we can do - everything else can not be handled correctly for now)
2260 // case three can not be handled correctly with layoutChanged - removeSections
2261 // should be used instead for this
2262 if (!hasPersistantIndexes) {
2263 if (oldCount != newCount)
2264 q->initializeSections();
2265 return;
2266 }
2267
2268 // adjust section size
2269 if (newCount != oldCount) {
2270 const int min = qBound(0, oldCount, newCount - 1);
2271 q->initializeSections(min, newCount - 1);
2272 }
2273 // reset sections
2274 sectionItems.fill(SectionItem(defaultSectionSize, globalResizeMode), newCount);
2275
2276 // all hidden sections are in oldPersistentSections
2277 hiddenSectionSize.clear();
2278
2279 for (const auto &item : oldPersistentSections) {
2280 const auto &index = item.index;
2281 if (!index.isValid())
2282 continue;
2283
2284 const int newLogicalIndex = (orientation == Qt::Horizontal
2285 ? index.column()
2286 : index.row());
2287 // the new visualIndices are already adjusted / reset by initializeSections()
2288 const int newVisualIndex = visualIndex(newLogicalIndex);
2289 if (newVisualIndex < sectionItems.count()) {
2290 auto &newSection = sectionItems[newVisualIndex];
2291 newSection = item.section;
2292
2293 if (newSection.isHidden) {
2294 // otherwise setSectionHidden will return without doing anything
2295 newSection.isHidden = false;
2296 q->setSectionHidden(newLogicalIndex, true);
2297 }
2298 }
2299 }
2300
2301 recalcSectionStartPos();
2302 length = headerLength();
2303
2304 if (stretchLastSection) {
2305 // force rebuild of stretched section later on
2306 lastSectionLogicalIdx = -1;
2307 maybeRestorePrevLastSectionAndStretchLast();
2308 }
2309}
2310
2311/*!
2312 \internal
2313*/
2314
2315void QHeaderView::initializeSections()
2316{
2317 Q_D(QHeaderView);
2318 const int oldCount = d->sectionCount();
2319 const int newCount = d->modelSectionCount();
2320 if (newCount <= 0) {
2321 d->clear();
2322 emit sectionCountChanged(oldCount, 0);
2323 } else if (newCount != oldCount) {
2324 const int min = qBound(0, oldCount, newCount - 1);
2325 initializeSections(min, newCount - 1);
2326 if (stretchLastSection()) // we've already gotten the size hint
2327 d->maybeRestorePrevLastSectionAndStretchLast();
2328
2329 // make sure we update the hidden sections
2330 // simulate remove from newCount to oldCount
2331 if (newCount < oldCount)
2332 d->updateHiddenSections(newCount, oldCount);
2333 }
2334}
2335
2336/*!
2337 \internal
2338*/
2339
2340void QHeaderView::initializeSections(int start, int end)
2341{
2342 Q_D(QHeaderView);
2343
2344 Q_ASSERT(start >= 0);
2345 Q_ASSERT(end >= 0);
2346
2347 d->invalidateCachedSizeHint();
2348 int oldCount = d->sectionCount();
2349
2350 if (end + 1 < d->sectionCount()) {
2351 int newCount = end + 1;
2352 d->removeSectionsFromSectionItems(newCount, d->sectionCount() - 1);
2353 if (!d->hiddenSectionSize.isEmpty()) {
2354 if (oldCount - newCount > d->hiddenSectionSize.count()) {
2355 for (int i = end + 1; i < d->sectionCount(); ++i)
2356 d->hiddenSectionSize.remove(i);
2357 } else {
2358 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
2359 while (it != d->hiddenSectionSize.end()) {
2360 if (it.key() > end)
2361 it = d->hiddenSectionSize.erase(it);
2362 else
2363 ++it;
2364 }
2365 }
2366 }
2367 }
2368
2369 int newSectionCount = end + 1;
2370
2371 if (!d->logicalIndices.isEmpty()) {
2372 if (oldCount <= newSectionCount) {
2373 d->logicalIndices.resize(newSectionCount);
2374 d->visualIndices.resize(newSectionCount);
2375 for (int i = oldCount; i < newSectionCount; ++i) {
2376 d->logicalIndices[i] = i;
2377 d->visualIndices[i] = i;
2378 }
2379 } else {
2380 int j = 0;
2381 for (int i = 0; i < oldCount; ++i) {
2382 int v = d->logicalIndices.at(i);
2383 if (v < newSectionCount) {
2384 d->logicalIndices[j] = v;
2385 d->visualIndices[v] = j;
2386 j++;
2387 }
2388 }
2389 d->logicalIndices.resize(newSectionCount);
2390 d->visualIndices.resize(newSectionCount);
2391 }
2392 }
2393
2394 if (d->globalResizeMode == Stretch)
2395 d->stretchSections = newSectionCount;
2396 else if (d->globalResizeMode == ResizeToContents)
2397 d->contentsSections = newSectionCount;
2398
2399 if (newSectionCount > oldCount)
2400 d->createSectionItems(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
2401 //Q_ASSERT(d->headerLength() == d->length);
2402
2403 if (d->sectionCount() != oldCount)
2404 emit sectionCountChanged(oldCount, d->sectionCount());
2405 d->viewport->update();
2406}
2407
2408/*!
2409 \reimp
2410*/
2411
2412void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
2413{
2414 Q_D(QHeaderView);
2415
2416 if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
2417 if (old.isValid() && old.parent() == d->root)
2418 d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
2419 sectionSize(old.column()), d->viewport->height()));
2420 if (current.isValid() && current.parent() == d->root)
2421 d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
2422 sectionSize(current.column()), d->viewport->height()));
2423 } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
2424 if (old.isValid() && old.parent() == d->root)
2425 d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
2426 d->viewport->width(), sectionSize(old.row())));
2427 if (current.isValid() && current.parent() == d->root)
2428 d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2429 d->viewport->width(), sectionSize(current.row())));
2430 }
2431}
2432
2433
2434/*!
2435 \reimp
2436*/
2437
2438bool QHeaderView::event(QEvent *e)
2439{
2440 Q_D(QHeaderView);
2441 switch (e->type()) {
2442 case QEvent::HoverEnter: {
2443 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2444 d->hover = logicalIndexAt(he->pos());
2445 if (d->hover != -1)
2446 updateSection(d->hover);
2447 break; }
2448 case QEvent::Leave:
2449 case QEvent::HoverLeave: {
2450 if (d->hover != -1)
2451 updateSection(d->hover);
2452 d->hover = -1;
2453 break; }
2454 case QEvent::HoverMove: {
2455 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2456 int oldHover = d->hover;
2457 d->hover = logicalIndexAt(he->pos());
2458 if (d->hover != oldHover) {
2459 if (oldHover != -1)
2460 updateSection(oldHover);
2461 if (d->hover != -1)
2462 updateSection(d->hover);
2463 }
2464 break; }
2465 case QEvent::Timer: {
2466 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2467 if (te->timerId() == d->delayedResize.timerId()) {
2468 d->delayedResize.stop();
2469 resizeSections();
2470 }
2471 break; }
2472 case QEvent::StyleChange:
2473 if (!d->customDefaultSectionSize)
2474 d->updateDefaultSectionSizeFromStyle();
2475 break;
2476 default:
2477 break;
2478 }
2479 return QAbstractItemView::event(e);
2480}
2481
2482/*!
2483 \reimp
2484*/
2485
2486void QHeaderView::paintEvent(QPaintEvent *e)
2487{
2488 Q_D(QHeaderView);
2489
2490 if (count() == 0)
2491 return;
2492
2493 QPainter painter(d->viewport);
2494 const QPoint offset = d->scrollDelayOffset;
2495 QRect translatedEventRect = e->rect();
2496 translatedEventRect.translate(offset);
2497
2498 int start = -1;
2499 int end = -1;
2500 if (d->orientation == Qt::Horizontal) {
2501 start = visualIndexAt(translatedEventRect.left());
2502 end = visualIndexAt(translatedEventRect.right());
2503 } else {
2504 start = visualIndexAt(translatedEventRect.top());
2505 end = visualIndexAt(translatedEventRect.bottom());
2506 }
2507
2508 if (d->reverse()) {
2509 start = (start == -1 ? count() - 1 : start);
2510 end = (end == -1 ? 0 : end);
2511 } else {
2512 start = (start == -1 ? 0 : start);
2513 end = (end == -1 ? count() - 1 : end);
2514 }
2515
2516 int tmp = start;
2517 start = qMin(start, end);
2518 end = qMax(tmp, end);
2519
2520 d->prepareSectionSelected(); // clear and resize the bit array
2521
2522 QRect currentSectionRect;
2523 const int width = d->viewport->width();
2524 const int height = d->viewport->height();
2525 const int rtlHorizontalOffset = d->reverse() ? 1 : 0;
2526 for (int i = start; i <= end; ++i) {
2527 if (d->isVisualIndexHidden(i))
2528 continue;
2529 painter.save();
2530 const int logical = logicalIndex(i);
2531 if (d->orientation == Qt::Horizontal) {
2532 currentSectionRect.setRect(sectionViewportPosition(logical) + rtlHorizontalOffset,
2533 0, sectionSize(logical), height);
2534 } else {
2535 currentSectionRect.setRect(0, sectionViewportPosition(logical),
2536 width, sectionSize(logical));
2537 }
2538 currentSectionRect.translate(offset);
2539
2540 QVariant variant = d->model->headerData(logical, d->orientation,
2541 Qt::FontRole);
2542 if (variant.isValid() && variant.canConvert<QFont>()) {
2543 QFont sectionFont = qvariant_cast<QFont>(variant);
2544 painter.setFont(sectionFont);
2545 }
2546 paintSection(&painter, currentSectionRect, logical);
2547 painter.restore();
2548 }
2549
2550 QStyleOption opt;
2551 opt.init(this);
2552 // Paint the area beyond where there are indexes
2553 if (d->reverse()) {
2554 opt.state |= QStyle::State_Horizontal;
2555 if (currentSectionRect.left() > translatedEventRect.left()) {
2556 opt.rect = QRect(translatedEventRect.left(), 0,
2557 currentSectionRect.left() - translatedEventRect.left(), height);
2558 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2559 }
2560 } else if (currentSectionRect.right() < translatedEventRect.right()) {
2561 // paint to the right
2562 opt.state |= QStyle::State_Horizontal;
2563 opt.rect = QRect(currentSectionRect.right() + 1, 0,
2564 translatedEventRect.right() - currentSectionRect.right(), height);
2565 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2566 } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2567 // paint the bottom section
2568 opt.state &= ~QStyle::State_Horizontal;
2569 opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2570 width, height - currentSectionRect.bottom() - 1);
2571 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2572 }
2573
2574#if 0
2575 // ### visualize sections
2576 for (int a = 0, i = 0; i < d->sectionItems.count(); ++i) {
2577 QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2578 if (d->orientation == Qt::Horizontal)
2579 painter.fillRect(a - d->offset, 0, d->sectionItems.at(i).size, 4, color);
2580 else
2581 painter.fillRect(0, a - d->offset, 4, d->sectionItems.at(i).size, color);
2582 a += d->sectionItems.at(i).size;
2583 }
2584
2585#endif
2586}
2587
2588/*!
2589 \reimp
2590*/
2591
2592void QHeaderView::mousePressEvent(QMouseEvent *e)
2593{
2594 Q_D(QHeaderView);
2595 if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2596 return;
2597 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2598 int handle = d->sectionHandleAt(pos);
2599 d->originalSize = -1; // clear the stored original size
2600 if (handle == -1) {
2601 d->pressed = logicalIndexAt(pos);
2602 if (d->clickableSections)
2603 emit sectionPressed(d->pressed);
2604
2605 bool acceptMoveSection = d->movableSections;
2606 if (acceptMoveSection && d->pressed == 0 && !d->allowUserMoveOfSection0)
2607 acceptMoveSection = false; // Do not allow moving the tree nod
2608
2609 if (acceptMoveSection) {
2610 d->section = d->target = d->pressed;
2611 if (d->section == -1)
2612 return;
2613 d->state = QHeaderViewPrivate::MoveSection;
2614 d->setupSectionIndicator(d->section, pos);
2615 } else if (d->clickableSections && d->pressed != -1) {
2616 updateSection(d->pressed);
2617 d->state = QHeaderViewPrivate::SelectSections;
2618 }
2619 } else if (sectionResizeMode(handle) == Interactive) {
2620 d->originalSize = sectionSize(handle);
2621 d->state = QHeaderViewPrivate::ResizeSection;
2622 d->section = handle;
2623 d->preventCursorChangeInSetOffset = false;
2624 }
2625
2626 d->firstPos = pos;
2627 d->lastPos = pos;
2628
2629 d->clearCascadingSections();
2630}
2631
2632/*!
2633 \reimp
2634*/
2635
2636void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2637{
2638 Q_D(QHeaderView);
2639 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2640 if (pos < 0 && d->state != QHeaderViewPrivate::SelectSections)
2641 return;
2642 if (e->buttons() == Qt::NoButton) {
2643#if 1 // Used to be excluded in Qt4 for Q_WS_MAC
2644 // Under Cocoa, when the mouse button is released, may include an extra
2645 // simulated mouse moved event. The state of the buttons when this event
2646 // is generated is already "no button" and the code below gets executed
2647 // just before the mouseReleaseEvent and resets the state. This prevents
2648 // column dragging from working. So this code is disabled under Cocoa.
2649 d->state = QHeaderViewPrivate::NoState;
2650 d->pressed = -1;
2651#endif
2652 }
2653 switch (d->state) {
2654 case QHeaderViewPrivate::ResizeSection: {
2655 Q_ASSERT(d->originalSize != -1);
2656 if (d->cascadingResizing) {
2657 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2658 int visual = visualIndex(d->section);
2659 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2660 } else {
2661 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2662 int newsize = qBound(minimumSectionSize(), d->originalSize + delta, maximumSectionSize());
2663 resizeSection(d->section, newsize);
2664 }
2665 d->lastPos = pos;
2666 return;
2667 }
2668 case QHeaderViewPrivate::MoveSection: {
2669 if (d->shouldAutoScroll(e->pos()))
2670 d->startAutoScroll();
2671 if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2672#if QT_CONFIG(label)
2673 || !d->sectionIndicator->isHidden()
2674#endif
2675 ) {
2676 int visual = visualIndexAt(pos);
2677 if (visual == -1)
2678 return;
2679 if (visual == 0 && logicalIndex(0) == 0 && !d->allowUserMoveOfSection0)
2680 return;
2681
2682 int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2;
2683 int moving = visualIndex(d->section);
2684 if (visual < moving) {
2685 if (pos < posThreshold)
2686 d->target = d->logicalIndex(visual);
2687 else
2688 d->target = d->logicalIndex(visual + 1);
2689 } else if (visual > moving) {
2690 if (pos > posThreshold)
2691 d->target = d->logicalIndex(visual);
2692 else
2693 d->target = d->logicalIndex(visual - 1);
2694 } else {
2695 d->target = d->section;
2696 }
2697 d->updateSectionIndicator(d->section, pos);
2698 }
2699 return;
2700 }
2701 case QHeaderViewPrivate::SelectSections: {
2702 int logical = logicalIndexAt(qMax(-d->offset, pos));
2703 if (logical == -1 && pos > 0)
2704 logical = logicalIndex(d->lastVisibleVisualIndex());
2705 if (logical == d->pressed)
2706 return; // nothing to do
2707 else if (d->pressed != -1)
2708 updateSection(d->pressed);
2709 d->pressed = logical;
2710 if (d->clickableSections && logical != -1) {
2711 emit sectionEntered(d->pressed);
2712 updateSection(d->pressed);
2713 }
2714 return;
2715 }
2716 case QHeaderViewPrivate::NoState: {
2717#ifndef QT_NO_CURSOR
2718 int handle = d->sectionHandleAt(pos);
2719 bool hasCursor = testAttribute(Qt::WA_SetCursor);
2720 if (handle != -1 && (sectionResizeMode(handle) == Interactive)) {
2721 if (!hasCursor)
2722 setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2723 } else {
2724 if (hasCursor)
2725 unsetCursor();
2726#ifndef QT_NO_STATUSTIP
2727 int logical = logicalIndexAt(pos);
2728 QString statusTip;
2729 if (logical != -1)
2730 statusTip = d->model->headerData(logical, d->orientation, Qt::StatusTipRole).toString();
2731 if (d->shouldClearStatusTip || !statusTip.isEmpty()) {
2732 QStatusTipEvent tip(statusTip);
2733 QCoreApplication::sendEvent(d->parent ? d->parent : this, &tip);
2734 d->shouldClearStatusTip = !statusTip.isEmpty();
2735 }
2736#endif // !QT_NO_STATUSTIP
2737 }
2738#endif
2739 return;
2740 }
2741 default:
2742 break;
2743 }
2744}
2745
2746/*!
2747 \reimp
2748*/
2749
2750void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2751{
2752 Q_D(QHeaderView);
2753 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2754 switch (d->state) {
2755 case QHeaderViewPrivate::MoveSection:
2756 if (true
2757#if QT_CONFIG(label)
2758 && !d->sectionIndicator->isHidden()
2759#endif
2760 ) { // moving
2761 int from = visualIndex(d->section);
2762 Q_ASSERT(from != -1);
2763 int to = visualIndex(d->target);
2764 Q_ASSERT(to != -1);
2765 moveSection(from, to);
2766 d->section = d->target = -1;
2767 d->updateSectionIndicator(d->section, pos);
2768 break;
2769 } // not moving
2770 Q_FALLTHROUGH();
2771 case QHeaderViewPrivate::SelectSections:
2772 if (!d->clickableSections) {
2773 int section = logicalIndexAt(pos);
2774 updateSection(section);
2775 }
2776 Q_FALLTHROUGH();
2777 case QHeaderViewPrivate::NoState:
2778 if (d->clickableSections) {
2779 int section = logicalIndexAt(pos);
2780 if (section != -1 && section == d->pressed) {
2781 d->flipSortIndicator(section);
2782 emit sectionClicked(section);
2783 }
2784 if (d->pressed != -1)
2785 updateSection(d->pressed);
2786 }
2787 break;
2788 case QHeaderViewPrivate::ResizeSection:
2789 d->originalSize = -1;
2790 d->clearCascadingSections();
2791 break;
2792 default:
2793 break;
2794 }
2795 d->state = QHeaderViewPrivate::NoState;
2796 d->pressed = -1;
2797}
2798
2799/*!
2800 \reimp
2801*/
2802
2803void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2804{
2805 Q_D(QHeaderView);
2806 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2807 int handle = d->sectionHandleAt(pos);
2808 if (handle > -1 && sectionResizeMode(handle) == Interactive) {
2809 emit sectionHandleDoubleClicked(handle);
2810#ifndef QT_NO_CURSOR
2811 Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2812 ? Qt::SplitHCursor : Qt::SplitVCursor;
2813 if (cursor().shape() == splitCursor) {
2814 // signal handlers may have changed the section size
2815 handle = d->sectionHandleAt(pos);
2816 if (!(handle > -1 && sectionResizeMode(handle) == Interactive))
2817 setCursor(Qt::ArrowCursor);
2818 }
2819#endif
2820 } else {
2821 emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2822 }
2823}
2824
2825/*!
2826 \reimp
2827*/
2828
2829bool QHeaderView::viewportEvent(QEvent *e)
2830{
2831 Q_D(QHeaderView);
2832 switch (e->type()) {
2833#ifndef QT_NO_TOOLTIP
2834 case QEvent::ToolTip: {
2835 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2836 int logical = logicalIndexAt(he->pos());
2837 if (logical != -1) {
2838 QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2839 if (variant.isValid()) {
2840 QToolTip::showText(he->globalPos(), variant.toString(), this);
2841 return true;
2842 }
2843 }
2844 break; }
2845#endif
2846#if QT_CONFIG(whatsthis)
2847 case QEvent::QueryWhatsThis: {
2848 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2849 int logical = logicalIndexAt(he->pos());
2850 if (logical != -1
2851 && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2852 return true;
2853 break; }
2854 case QEvent::WhatsThis: {
2855 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2856 int logical = logicalIndexAt(he->pos());
2857 if (logical != -1) {
2858 QVariant whatsthis = d->model->headerData(logical, d->orientation,
2859 Qt::WhatsThisRole);
2860 if (whatsthis.isValid()) {
2861 QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2862 return true;
2863 }
2864 }
2865 break; }
2866#endif // QT_CONFIG(whatsthis)
2867#if QT_CONFIG(statustip)
2868 case QEvent::StatusTip: {
2869 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2870 int logical = logicalIndexAt(he->pos());
2871 if (logical != -1) {
2872 QString statustip = d->model->headerData(logical, d->orientation,
2873 Qt::StatusTipRole).toString();
2874 if (!statustip.isEmpty())
2875 setStatusTip(statustip);
2876 }
2877 return true; }
2878#endif // QT_CONFIG(statustip)
2879 case QEvent::Resize:
2880 case QEvent::FontChange:
2881 case QEvent::StyleChange:
2882 d->invalidateCachedSizeHint();
2883 Q_FALLTHROUGH();
2884 case QEvent::Hide:
2885 case QEvent::Show: {
2886 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
2887 if (parent && parent->isVisible()) // Only resize if we have a visible parent
2888 resizeSections();
2889 emit geometriesChanged();
2890 break;}
2891 case QEvent::ContextMenu: {
2892 d->state = QHeaderViewPrivate::NoState;
2893 d->pressed = d->section = d->target = -1;
2894 d->updateSectionIndicator(d->section, -1);
2895 break; }
2896 case QEvent::Wheel: {
2897 QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2898 if (asa)
2899 return QCoreApplication::sendEvent(asa->viewport(), e);
2900 break; }
2901 default:
2902 break;
2903 }
2904 return QAbstractItemView::viewportEvent(e);
2905}
2906
2907/*!
2908 Paints the section specified by the given \a logicalIndex, using the given
2909 \a painter and \a rect.
2910
2911 Normally, you do not have to call this function.
2912*/
2913
2914void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2915{
2916 Q_D(const QHeaderView);
2917 if (!rect.isValid())
2918 return;
2919 // get the state of the section
2920 QStyleOptionHeader opt;
2921 initStyleOption(&opt);
2922 QStyle::State state = QStyle::State_None;
2923 if (isEnabled())
2924 state |= QStyle::State_Enabled;
2925 if (window()->isActiveWindow())
2926 state |= QStyle::State_Active;
2927 if (d->clickableSections) {
2928 if (logicalIndex == d->hover)
2929 state |= QStyle::State_MouseOver;
2930 if (logicalIndex == d->pressed)
2931 state |= QStyle::State_Sunken;
2932 else if (d->highlightSelected) {
2933 if (d->sectionIntersectsSelection(logicalIndex))
2934 state |= QStyle::State_On;
2935 if (d->isSectionSelected(logicalIndex))
2936 state |= QStyle::State_Sunken;
2937 }
2938
2939 }
2940 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2941 opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2942 ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2943
2944 // setup the style options structure
2945 QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2946 Qt::TextAlignmentRole);
2947 opt.rect = rect;
2948 opt.section = logicalIndex;
2949 opt.state |= state;
2950 opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2951 ? Qt::Alignment(textAlignment.toInt())
2952 : d->defaultAlignment);
2953
2954 opt.iconAlignment = Qt::AlignVCenter;
2955 opt.text = d->model->headerData(logicalIndex, d->orientation,
2956 Qt::DisplayRole).toString();
2957
2958 int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
2959
2960 const Qt::Alignment headerArrowAlignment = static_cast<Qt::Alignment>(style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this));
2961 const bool isHeaderArrowOnTheSide = headerArrowAlignment & Qt::AlignVCenter;
2962 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex && isHeaderArrowOnTheSide)
2963 margin += style()->pixelMetric(QStyle::PM_HeaderMarkSize, 0, this);
2964
2965 const QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2966 Qt::DecorationRole);
2967 opt.icon = qvariant_cast<QIcon>(variant);
2968 if (opt.icon.isNull())
2969 opt.icon = qvariant_cast<QPixmap>(variant);
2970 if (!opt.icon.isNull()) // see CT_HeaderSection
2971 margin += style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this) +
2972 style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
2973
2974 if (d->textElideMode != Qt::ElideNone) {
2975 const QRect textRect = style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this);
2976 opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode, textRect.width() - margin);
2977 }
2978
2979 QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2980 Qt::ForegroundRole);
2981 if (foregroundBrush.canConvert<QBrush>())
2982 opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2983
2984 QPointF oldBO = painter->brushOrigin();
2985 QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2986 Qt::BackgroundRole);
2987 if (backgroundBrush.canConvert<QBrush>()) {
2988 opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2989 opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2990 painter->setBrushOrigin(opt.rect.topLeft());
2991 }
2992
2993 // the section position
2994 int visual = visualIndex(logicalIndex);
2995 Q_ASSERT(visual != -1);
2996 bool first = d->isFirstVisibleSection(visual);
2997 bool last = d->isLastVisibleSection(visual);
2998 if (first && last)
2999 opt.position = QStyleOptionHeader::OnlyOneSection;
3000 else if (first)
3001 opt.position = d->reverse() ? QStyleOptionHeader::End : QStyleOptionHeader::Beginning;
3002 else if (last)
3003 opt.position = d->reverse() ? QStyleOptionHeader::Beginning : QStyleOptionHeader::End;
3004 else
3005 opt.position = QStyleOptionHeader::Middle;
3006 opt.orientation = d->orientation;
3007 // the selected position
3008 bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
3009 bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
3010 if (previousSelected && nextSelected)
3011 opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
3012 else if (previousSelected)
3013 opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
3014 else if (nextSelected)
3015 opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
3016 else
3017 opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
3018 // draw the section
3019 style()->drawControl(QStyle::CE_Header, &opt, painter, this);
3020
3021 painter->setBrushOrigin(oldBO);
3022}
3023
3024/*!
3025 Returns the size of the contents of the section specified by the given
3026 \a logicalIndex.
3027
3028 \sa defaultSectionSize()
3029*/
3030
3031QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
3032{
3033 Q_D(const QHeaderView);
3034 Q_ASSERT(logicalIndex >= 0);
3035
3036 ensurePolished();
3037
3038 // use SizeHintRole
3039 QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
3040 if (variant.isValid())
3041 return qvariant_cast<QSize>(variant);
3042
3043 // otherwise use the contents
3044 QStyleOptionHeader opt;
3045 initStyleOption(&opt);
3046 opt.section = logicalIndex;
3047 QVariant var = d->model->headerData(logicalIndex, d->orientation,
3048 Qt::FontRole);
3049 QFont fnt;
3050 if (var.isValid() && var.canConvert<QFont>())
3051 fnt = qvariant_cast<QFont>(var);
3052 else
3053 fnt = font();
3054 fnt.setBold(true);
3055 opt.fontMetrics = QFontMetrics(fnt);
3056 opt.text = d->model->headerData(logicalIndex, d->orientation,
3057 Qt::DisplayRole).toString();
3058 variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
3059 opt.icon = qvariant_cast<QIcon>(variant);
3060 if (opt.icon.isNull())
3061 opt.icon = qvariant_cast<QPixmap>(variant);
3062 if (isSortIndicatorShown())
3063 opt.sortIndicator = QStyleOptionHeader::SortDown;
3064 return style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
3065}
3066
3067/*!
3068 Returns the horizontal offset of the header. This is 0 for vertical
3069 headers.
3070
3071 \sa offset()
3072*/
3073
3074int QHeaderView::horizontalOffset() const
3075{
3076 Q_D(const QHeaderView);
3077 if (d->orientation == Qt::Horizontal)
3078 return d->offset;
3079 return 0;
3080}
3081
3082/*!
3083 Returns the vertical offset of the header. This is 0 for horizontal
3084 headers.
3085
3086 \sa offset()
3087*/
3088
3089int QHeaderView::verticalOffset() const
3090{
3091 Q_D(const QHeaderView);
3092 if (d->orientation == Qt::Vertical)
3093 return d->offset;
3094 return 0;
3095}
3096
3097/*!
3098 \reimp
3099 \internal
3100*/
3101
3102void QHeaderView::updateGeometries()
3103{
3104 Q_D(QHeaderView);
3105 d->layoutChildren();
3106 if (d->hasAutoResizeSections())
3107 d->doDelayedResizeSections();
3108}
3109
3110/*!
3111 \reimp
3112 \internal
3113*/
3114
3115void QHeaderView::scrollContentsBy(int dx, int dy)
3116{
3117 Q_D(QHeaderView);
3118 d->scrollDirtyRegion(dx, dy);
3119}
3120
3121/*!
3122 \reimp
3123 \internal
3124*/
3125void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
3126{
3127 Q_D(QHeaderView);
3128 if (!roles.isEmpty()) {
3129 const auto doesRoleAffectSize = [](int role) -> bool {
3130 switch (role) {
3131 case Qt::DisplayRole:
3132 case Qt::DecorationRole:
3133 case Qt::SizeHintRole:
3134 case Qt::FontRole:
3135 return true;
3136 default:
3137 // who knows what a subclass or custom style might do
3138 return role >= Qt::UserRole;
3139 }
3140 };
3141 if (std::none_of(roles.begin(), roles.end(), doesRoleAffectSize))
3142 return;
3143 }
3144 d->invalidateCachedSizeHint();
3145 if (d->hasAutoResizeSections()) {
3146 bool resizeRequired = d->globalResizeMode == ResizeToContents;
3147 int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
3148 int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
3149 for (int i = first; i <= last && !resizeRequired; ++i)
3150 resizeRequired = (sectionResizeMode(i) == ResizeToContents);
3151 if (resizeRequired)
3152 d->doDelayedResizeSections();
3153 }
3154}
3155
3156/*!
3157 \reimp
3158 \internal
3159
3160 Empty implementation because the header doesn't show QModelIndex items.
3161*/
3162void QHeaderView::rowsInserted(const QModelIndex &, int, int)
3163{
3164 // do nothing
3165}
3166
3167/*!
3168 \reimp
3169 \internal
3170
3171 Empty implementation because the header doesn't show QModelIndex items.
3172*/
3173
3174QRect QHeaderView::visualRect(const QModelIndex &) const
3175{
3176 return QRect();
3177}
3178
3179/*!
3180 \reimp
3181 \internal
3182
3183 Empty implementation because the header doesn't show QModelIndex items.
3184*/
3185
3186void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
3187{
3188 // do nothing - the header only displays sections
3189}
3190
3191/*!
3192 \reimp
3193 \internal
3194
3195 Empty implementation because the header doesn't show QModelIndex items.
3196*/
3197
3198QModelIndex QHeaderView::indexAt(const QPoint &) const
3199{
3200 return QModelIndex();
3201}
3202
3203/*!
3204 \reimp
3205 \internal
3206
3207 Empty implementation because the header doesn't show QModelIndex items.
3208*/
3209
3210bool QHeaderView::isIndexHidden(const QModelIndex &) const
3211{
3212 return true; // the header view has no items, just sections
3213}
3214
3215/*!
3216 \reimp
3217 \internal
3218
3219 Empty implementation because the header doesn't show QModelIndex items.
3220*/
3221
3222QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
3223{
3224 return QModelIndex();
3225}
3226
3227/*!
3228 \reimp
3229
3230 Selects the items in the given \a rect according to the specified
3231 \a flags.
3232
3233 The base class implementation does nothing.
3234*/
3235
3236void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
3237{
3238 // do nothing
3239}
3240
3241/*!
3242 \internal
3243*/
3244
3245QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
3246{
3247 Q_D(const QHeaderView);
3248 const int max = d->modelSectionCount();
3249
3250 if (d->orientation == Qt::Horizontal) {
3251 int logicalLeft = max;
3252 int logicalRight = 0;
3253
3254 if (d->visualIndices.empty()) {
3255 // If no reordered sections, skip redundant visual-to-logical transformations
3256 for (const auto &r :