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:
150 \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
151 \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
152 \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
153
154 \note Each header renders the data for each section itself, and does not
155 rely on a delegate. As a result, calling a header's setItemDelegate()
156 function will have no effect.
157
158 \sa {Model/View Programming}, QListView, QTableView, QTreeView
159*/
160
161/*!
162 \enum QHeaderView::ResizeMode
163
164 The resize mode specifies the behavior of the header sections. It can be
165 set on the entire header view or on individual sections using
166 setSectionResizeMode().
167
168 \value Interactive The user can resize the section. The section can also be
169 resized programmatically using resizeSection(). The section size
170 defaults to \l defaultSectionSize. (See also
171 \l cascadingSectionResizes.)
172
173 \value Fixed The user cannot resize the section. The section can only be
174 resized programmatically using resizeSection(). The section size
175 defaults to \l defaultSectionSize.
176
177 \value Stretch QHeaderView will automatically resize the section to fill
178 the available space. The size cannot be changed by the user or
179 programmatically.
180
181 \value ResizeToContents QHeaderView will automatically resize the section
182 to its optimal size based on the contents of the entire column or
183 row. The size cannot be changed by the user or programmatically.
184 (This value was introduced in 4.2)
185
186 The following values are obsolete:
187 \value Custom Use Fixed instead.
188
189 \sa setResizeMode(), setSectionResizeMode(), stretchLastSection, minimumSectionSize
190*/
191
192/*!
193 \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
194 int newVisualIndex)
195
196 This signal is emitted when a section is moved. The section's logical index
197 is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
198 the new index position by \a newVisualIndex.
199
200 \sa moveSection()
201*/
202
203/*!
204 \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
205 int newSize)
206
207 This signal is emitted when a section is resized. The section's logical
208 number is specified by \a logicalIndex, the old size by \a oldSize, and the
209 new size by \a newSize.
210
211 \sa resizeSection()
212*/
213
214/*!
215 \fn void QHeaderView::sectionPressed(int logicalIndex)
216
217 This signal is emitted when a section is pressed. The section's logical
218 index is specified by \a logicalIndex.
219
220 \sa setSectionsClickable()
221*/
222
223/*!
224 \fn void QHeaderView::sectionClicked(int logicalIndex)
225
226 This signal is emitted when a section is clicked. The section's logical
227 index is specified by \a logicalIndex.
228
229 Note that the sectionPressed signal will also be emitted.
230
231 \sa setSectionsClickable(), sectionPressed()
232*/
233
234/*!
235 \fn void QHeaderView::sectionEntered(int logicalIndex)
236 \since 4.3
237
238 This signal is emitted when the cursor moves over the section and the left
239 mouse button is pressed. The section's logical index is specified by
240 \a logicalIndex.
241
242 \sa setSectionsClickable(), sectionPressed()
243*/
244
245/*!
246 \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
247
248 This signal is emitted when a section is double-clicked. The section's
249 logical index is specified by \a logicalIndex.
250
251 \sa setSectionsClickable()
252*/
253
254/*!
255 \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
256
257 This signal is emitted when the number of sections changes, i.e., when
258 sections are added or deleted. The original count is specified by
259 \a oldCount, and the new count by \a newCount.
260
261 \sa count(), length(), headerDataChanged()
262*/
263
264/*!
265 \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
266
267 This signal is emitted when a section is double-clicked. The section's
268 logical index is specified by \a logicalIndex.
269
270 \sa setSectionsClickable()
271*/
272
273/*!
274 \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
275 Qt::SortOrder order)
276 \since 4.3
277
278 This signal is emitted when the section containing the sort indicator or
279 the order indicated is changed. The section's logical index is specified
280 by \a logicalIndex and the sort order is specified by \a order.
281
282 \sa setSortIndicator()
283*/
284
285/*!
286 \fn void QHeaderView::geometriesChanged()
287 \since 4.2
288
289 This signal is emitted when the header's geometries have changed.
290*/
291
292/*!
293 \property QHeaderView::highlightSections
294 \brief whether the sections containing selected items are highlighted
295
296 By default, this property is \c false.
297*/
298
299/*!
300 Creates a new generic header with the given \a orientation and \a parent.
301*/
302QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
303 : QAbstractItemView(*new QHeaderViewPrivate, parent)
304{
305 Q_D(QHeaderView);
306 d->setDefaultValues(orientation);
307 initialize();
308}
309
310/*!
311 \internal
312*/
313QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
314 Qt::Orientation orientation, QWidget *parent)
315 : QAbstractItemView(dd, parent)
316{
317 Q_D(QHeaderView);
318 d->setDefaultValues(orientation);
319 initialize();
320}
321
322/*!
323 Destroys the header.
324*/
325
326QHeaderView::~QHeaderView()
327{
328}
329
330/*!
331 \internal
332*/
333void QHeaderView::initialize()
334{
335 Q_D(QHeaderView);
336 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
337 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
338 setFrameStyle(NoFrame);
339 setFocusPolicy(Qt::NoFocus);
340 d->viewport->setMouseTracking(true);
341 d->viewport->setBackgroundRole(QPalette::Button);
342 d->textElideMode = Qt::ElideNone;
343 delete d->itemDelegate;
344}
345
346/*!
347 \reimp
348*/
349void QHeaderView::setModel(QAbstractItemModel *model)
350{
351 if (model == this->model())
352 return;
353 Q_D(QHeaderView);
354 d->layoutChangePersistentSections.clear();
355 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
356 if (d->orientation == Qt::Horizontal) {
357 QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
358 this, SLOT(sectionsInserted(QModelIndex,int,int)));
359 QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
360 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
361 QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
362 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
363 QObject::disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
364 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
365 QObject::disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
366 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
367 } else {
368 QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
369 this, SLOT(sectionsInserted(QModelIndex,int,int)));
370 QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
371 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
372 QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
373 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
374 QObject::disconnect(d->model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
375 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
376 QObject::disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
377 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
378 }
379 QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
380 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
381 QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
382 this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
383 QObject::disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
384 this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
385 }
386
387 if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
388 if (d->orientation == Qt::Horizontal) {
389 QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
390 this, SLOT(sectionsInserted(QModelIndex,int,int)));
391 QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
392 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
393 QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
394 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
395 QObject::connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
396 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
397 QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
398 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
399 } else {
400 QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
401 this, SLOT(sectionsInserted(QModelIndex,int,int)));
402 QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
403 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
404 QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
405 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
406 QObject::connect(model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
407 this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)));
408 QObject::connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
409 this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int)));
410 }
411 QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
412 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
413 QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
414 this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
415 QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
416 this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
417 }
418
419 d->state = QHeaderViewPrivate::NoClear;
420 QAbstractItemView::setModel(model);
421 d->state = QHeaderViewPrivate::NoState;
422
423 // Users want to set sizes and modes before the widget is shown.
424 // Thus, we have to initialize when the model is set,
425 // and not lazily like we do in the other views.
426 initializeSections();
427}
428
429/*!
430 Returns the orientation of the header.
431
432 \sa Qt::Orientation
433*/
434
435Qt::Orientation QHeaderView::orientation() const
436{
437 Q_D(const QHeaderView);
438 return d->orientation;
439}
440
441/*!
442 Returns the offset of the header: this is the header's left-most (or
443 top-most for vertical headers) visible pixel.
444
445 \sa setOffset()
446*/
447
448int QHeaderView::offset() const
449{
450 Q_D(const QHeaderView);
451 return d->offset;
452}
453
454/*!
455 \fn void QHeaderView::setOffset(int offset)
456
457 Sets the header's offset to \a offset.
458
459 \sa offset(), length()
460*/
461
462void QHeaderView::setOffset(int newOffset)
463{
464 Q_D(QHeaderView);
465 if (d->offset == (int)newOffset)
466 return;
467 int ndelta = d->offset - newOffset;
468 d->offset = newOffset;
469 if (d->orientation == Qt::Horizontal)
470 d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
471 else
472 d->viewport->scroll(0, ndelta);
473 if (d->state == QHeaderViewPrivate::ResizeSection && !d->preventCursorChangeInSetOffset) {
474 QPoint cursorPos = QCursor::pos();
475 if (d->orientation == Qt::Horizontal)
476 QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
477 else
478 QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
479 d->firstPos += ndelta;
480 d->lastPos += ndelta;
481 }
482}
483
484/*!
485 \since 4.2
486 Sets the offset to the start of the section at the given \a visualSectionNumber.
487 \a visualSectionNumber is the actual visible section when hiddenSections are
488 not considered. That is not always the same as visualIndex().
489
490 \sa setOffset(), sectionPosition()
491*/
492void QHeaderView::setOffsetToSectionPosition(int visualSectionNumber)
493{
494 Q_D(QHeaderView);
495 if (visualSectionNumber > -1 && visualSectionNumber < d->sectionCount()) {
496 int position = d->headerSectionPosition(d->adjustedVisualIndex(visualSectionNumber));
497 setOffset(position);
498 }
499}
500
501/*!
502 \since 4.2
503 Sets the offset to make the last section visible.
504
505 \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
506*/
507void QHeaderView::setOffsetToLastSection()
508{
509 Q_D(const QHeaderView);
510 int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
511 int position = length() - size;
512 setOffset(position);
513}
514
515/*!
516 Returns the length along the orientation of the header.
517
518 \sa sizeHint(), setSectionResizeMode(), offset()
519*/
520
521int QHeaderView::length() const
522{
523 Q_D(const QHeaderView);
524 d->executePostedLayout();
525 d->executePostedResize();
526 //Q_ASSERT(d->headerLength() == d->length);
527 return d->length;
528}
529
530/*!
531 Returns a suitable size hint for this header.
532
533 \sa sectionSizeHint()
534*/
535
536QSize QHeaderView::sizeHint() const
537{
538 Q_D(const QHeaderView);
539 if (d->cachedSizeHint.isValid())
540 return d->cachedSizeHint;
541 d->cachedSizeHint = QSize(0, 0); //reinitialize the cached size hint
542 const int sectionCount = count();
543
544 // get size hint for the first n sections
545 int i = 0;
546 for (int checked = 0; checked < 100 && i < sectionCount; ++i) {
547 if (isSectionHidden(i))
548 continue;
549 checked++;
550 QSize hint = sectionSizeFromContents(i);
551 d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
552 }
553 // get size hint for the last n sections
554 i = qMax(i, sectionCount - 100 );
555 for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) {
556 if (isSectionHidden(j))
557 continue;
558 checked++;
559 QSize hint = sectionSizeFromContents(j);
560 d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
561 }
562 return d->cachedSizeHint;
563}
564
565/*!
566 \reimp
567*/
568
569void QHeaderView::setVisible(bool v)
570{
571 bool actualChange = (v != isVisible());
572 QAbstractItemView::setVisible(v);
573 if (actualChange) {
574 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget());
575 if (parent)
576 parent->updateGeometry();
577 }
578}
579
580
581/*!
582 Returns a suitable size hint for the section specified by \a logicalIndex.
583
584 \sa sizeHint(), defaultSectionSize(), minimumSectionSize(), maximumSectionSize()
585 Qt::SizeHintRole
586*/
587
588int QHeaderView::sectionSizeHint(int logicalIndex) const
589{
590 Q_D(const QHeaderView);
591 if (isSectionHidden(logicalIndex))
592 return 0;
593 if (logicalIndex < 0 || logicalIndex >= count())
594 return -1;
595 QSize size;
596 QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
597 if (value.isValid())
598 size = qvariant_cast<QSize>(value);
599 else
600 size = sectionSizeFromContents(logicalIndex);
601 int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
602 return qBound(minimumSectionSize(), hint, maximumSectionSize());
603}
604
605/*!
606 Returns the visual index of the section that covers the given \a position
607 in the viewport.
608
609 \sa logicalIndexAt()
610*/
611
612int QHeaderView::visualIndexAt(int position) const
613{
614 Q_D(const QHeaderView);
615 int vposition = position;
616 d->executePostedLayout();
617 d->executePostedResize();
618 const int count = d->sectionCount();
619 if (count < 1)
620 return -1;
621
622 if (d->reverse())
623 vposition = d->viewport->width() - vposition - 1;
624 vposition += d->offset;
625
626 if (vposition > d->length)
627 return -1;
628 int visual = d->headerVisualIndexAt(vposition);
629 if (visual < 0)
630 return -1;
631
632 while (d->isVisualIndexHidden(visual)){
633 ++visual;
634 if (visual >= count)
635 return -1;
636 }
637 return visual;
638}
639
640/*!
641 Returns the section that covers the given \a position in the viewport.
642
643 \sa visualIndexAt(), isSectionHidden()
644*/
645
646int QHeaderView::logicalIndexAt(int position) const
647{
648 const int visual = visualIndexAt(position);
649 if (visual > -1)
650 return logicalIndex(visual);
651 return -1;
652}
653
654/*!
655 Returns the width (or height for vertical headers) of the given
656 \a logicalIndex.
657
658 \sa length(), setSectionResizeMode(), defaultSectionSize()
659*/
660
661int QHeaderView::sectionSize(int logicalIndex) const
662{
663 Q_D(const QHeaderView);
664 if (isSectionHidden(logicalIndex))
665 return 0;
666 if (logicalIndex < 0 || logicalIndex >= count())
667 return 0;
668 int visual = visualIndex(logicalIndex);
669 if (visual == -1)
670 return 0;
671 d->executePostedResize();
672 return d->headerSectionSize(visual);
673}
674
675/*!
676
677 Returns the section position of the given \a logicalIndex, or -1
678 if the section is hidden. The position is measured in pixels from
679 the first visible item's top-left corner to the top-left corner of
680 the item with \a logicalIndex. The measurement is along the x-axis
681 for horizontal headers and along the y-axis for vertical headers.
682
683 \sa sectionViewportPosition()
684*/
685
686int QHeaderView::sectionPosition(int logicalIndex) const
687{
688 Q_D(const QHeaderView);
689 int visual = visualIndex(logicalIndex);
690 // in some cases users may change the selections
691 // before we have a chance to do the layout
692 if (visual == -1)
693 return -1;
694 d->executePostedResize();
695 return d->headerSectionPosition(visual);
696}
697
698/*!
699 Returns the section viewport position of the given \a logicalIndex.
700
701 If the section is hidden, the return value is undefined.
702
703 \sa sectionPosition(), isSectionHidden()
704*/
705
706int QHeaderView::sectionViewportPosition(int logicalIndex) const
707{
708 Q_D(const QHeaderView);
709 if (logicalIndex >= count())
710 return -1;
711 int position = sectionPosition(logicalIndex);
712 if (position < 0)
713 return position; // the section was hidden
714 int offsetPosition = position - d->offset;
715 if (d->reverse())
716 return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
717 return offsetPosition;
718}
719
720/*!
721 \fn int QHeaderView::logicalIndexAt(int x, int y) const
722
723 Returns the logical index of the section at the given coordinate. If the
724 header is horizontal \a x will be used, otherwise \a y will be used to
725 find the logical index.
726*/
727
728/*!
729 \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
730
731 Returns the logical index of the section at the position given in \a pos.
732 If the header is horizontal the x-coordinate will be used, otherwise the
733 y-coordinate will be used to find the logical index.
734
735 \sa sectionPosition()
736*/
737
738template<typename Container>
739static void qMoveRange(Container& c,
740 typename Container::size_type rangeStart,
741 typename Container::size_type rangeEnd,
742 typename Container::size_type targetPosition)
743{
744 Q_ASSERT(targetPosition <= c.size());
745 Q_ASSERT(targetPosition < rangeStart || targetPosition >= rangeEnd);
746
747 const bool forwardMove = targetPosition > rangeStart;
748 typename Container::size_type first = std::min(rangeStart, targetPosition);
749 typename Container::size_type mid = forwardMove ? rangeEnd : rangeStart;
750 typename Container::size_type last = forwardMove ? targetPosition + 1 : rangeEnd;
751 std::rotate(c.begin() + first, c.begin() + mid, c.begin() + last);
752}
753
754/*!
755 Moves the section at visual index \a from to occupy visual index \a to.
756
757 \sa sectionsMoved()
758*/
759
760void QHeaderView::moveSection(int from, int to)
761{
762 Q_D(QHeaderView);
763
764 d->executePostedLayout();
765 if (from < 0 || from >= d->sectionCount() || to < 0 || to >= d->sectionCount())
766 return;
767
768 if (from == to) {
769 int logical = logicalIndex(from);
770 Q_ASSERT(logical != -1);
771 updateSection(logical);
772 return;
773 }
774
775 d->initializeIndexMapping();
776
777 int *visualIndices = d->visualIndices.data();
778 int *logicalIndices = d->logicalIndices.data();
779 int logical = logicalIndices[from];
780 int visual = from;
781
782 if (to > from) {
783 while (visual < to) {
784 visualIndices[logicalIndices[visual + 1]] = visual;
785 logicalIndices[visual] = logicalIndices[visual + 1];
786 ++visual;
787 }
788 } else {
789 while (visual > to) {
790 visualIndices[logicalIndices[visual - 1]] = visual;
791 logicalIndices[visual] = logicalIndices[visual - 1];
792 --visual;
793 }
794 }
795 visualIndices[logical] = to;
796 logicalIndices[to] = logical;
797
798 qMoveRange(d->sectionItems, from, from + 1, to);
799
800 d->sectionStartposRecalc = true;
801
802 if (d->hasAutoResizeSections())
803 d->doDelayedResizeSections();
804 d->viewport->update();
805
806 emit sectionMoved(logical, from, to);
807
808 if (stretchLastSection()) {
809 const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx);
810 if (from >= lastSectionVisualIdx || to >= lastSectionVisualIdx)
811 d->maybeRestorePrevLastSectionAndStretchLast();
812 }
813}
814
815/*!
816 \since 4.2
817 Swaps the section at visual index \a first with the section at visual
818 index \a second.
819
820 \sa moveSection()
821*/
822void QHeaderView::swapSections(int first, int second)
823{
824 Q_D(QHeaderView);
825
826 if (first == second)
827 return;
828 d->executePostedLayout();
829 if (first < 0 || first >= d->sectionCount() || second < 0 || second >= d->sectionCount())
830 return;
831
832 int firstSize = d->headerSectionSize(first);
833 ResizeMode firstMode = d->headerSectionResizeMode(first);
834 int firstLogical = d->logicalIndex(first);
835
836 int secondSize = d->headerSectionSize(second);
837 ResizeMode secondMode = d->headerSectionResizeMode(second);
838 int secondLogical = d->logicalIndex(second);
839
840 if (d->state == QHeaderViewPrivate::ResizeSection)
841 d->preventCursorChangeInSetOffset = true;
842
843 d->createSectionItems(second, second, firstSize, firstMode);
844 d->createSectionItems(first, first, secondSize, secondMode);
845
846 d->initializeIndexMapping();
847
848 d->visualIndices[firstLogical] = second;
849 d->logicalIndices[second] = firstLogical;
850
851 d->visualIndices[secondLogical] = first;
852 d->logicalIndices[first] = secondLogical;
853
854 if (!d->hiddenSectionSize.isEmpty()) {
855 bool firstHidden = d->isVisualIndexHidden(first);
856 bool secondHidden = d->isVisualIndexHidden(second);
857 d->setVisualIndexHidden(first, secondHidden);
858 d->setVisualIndexHidden(second, firstHidden);
859 }
860
861 d->viewport->update();
862 emit sectionMoved(firstLogical, first, second);
863 emit sectionMoved(secondLogical, second, first);
864
865 if (stretchLastSection()) {
866 const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx);
867 if (first >= lastSectionVisualIdx || second >= lastSectionVisualIdx)
868 d->maybeRestorePrevLastSectionAndStretchLast();
869 }
870}
871
872/*!
873 \fn void QHeaderView::resizeSection(int logicalIndex, int size)
874
875 Resizes the section specified by \a logicalIndex to \a size measured in
876 pixels. The size parameter must be a value larger or equal to zero. A
877 size equal to zero is however not recommended. In that situation hideSection
878 should be used instead.
879
880 \sa sectionResized(), resizeMode(), sectionSize(), hideSection()
881*/
882
883void QHeaderView::resizeSection(int logical, int size)
884{
885 Q_D(QHeaderView);
886 if (logical < 0 || logical >= count() || size < 0 || size > maxSizeSection)
887 return;
888
889 // make sure to not exceed bounds when setting size programmatically
890 if (size > 0)
891 size = qBound(minimumSectionSize(), size, maximumSectionSize());
892
893 if (isSectionHidden(logical)) {
894 d->hiddenSectionSize.insert(logical, size);
895 return;
896 }
897
898 int visual = visualIndex(logical);
899 if (visual == -1)
900 return;
901
902 if (d->state == QHeaderViewPrivate::ResizeSection && !d->cascadingResizing && logical != d->section)
903 d->preventCursorChangeInSetOffset = true;
904
905 int oldSize = d->headerSectionSize(visual);
906 if (oldSize == size)
907 return;
908
909 d->executePostedLayout();
910 d->invalidateCachedSizeHint();
911
912 if (stretchLastSection() && logical == d->lastSectionLogicalIdx)
913 d->lastSectionSize = size;
914
915 d->createSectionItems(visual, visual, size, d->headerSectionResizeMode(visual));
916
917 if (!updatesEnabled()) {
918 if (d->hasAutoResizeSections())
919 d->doDelayedResizeSections();
920 emit sectionResized(logical, oldSize, size);
921 return;
922 }
923
924 int w = d->viewport->width();
925 int h = d->viewport->height();
926 int pos = sectionViewportPosition(logical);
927 QRect r;
928 if (d->orientation == Qt::Horizontal)
929 if (isRightToLeft())
930 r.setRect(0, 0, pos + size, h);
931 else
932 r.setRect(pos, 0, w - pos, h);
933 else
934 r.setRect(0, pos, w, h - pos);
935
936 if (d->hasAutoResizeSections()) {
937 d->doDelayedResizeSections();
938 r = d->viewport->rect();
939 }
940
941 // If the parent is a QAbstractScrollArea with QAbstractScrollArea::AdjustToContents
942 // then we want to change the geometry on that widget. Not doing it at once can/will
943 // cause scrollbars flicker as they would be shown at first but then removed.
944 // In the same situation it will also allow shrinking the whole view when stretchLastSection is set
945 // (It is default on QTreeViews - and it wouldn't shrink since the last stretch was made before the
946 // viewport was resized)
947
948 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
949 if (parent && parent->sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents)
950 parent->updateGeometry();
951
952 d->viewport->update(r.normalized());
953 emit sectionResized(logical, oldSize, size);
954}
955
956/*!
957 Resizes the sections according to the given \a mode, ignoring the current
958 resize mode.
959
960 \sa resizeMode(), sectionResized()
961*/
962
963void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
964{
965 Q_D(QHeaderView);
966 d->resizeSections(mode, true);
967}
968
969/*!
970 \fn void QHeaderView::hideSection(int logicalIndex)
971 Hides the section specified by \a logicalIndex.
972
973 \sa showSection(), isSectionHidden(), hiddenSectionCount(),
974 setSectionHidden()
975*/
976
977/*!
978 \fn void QHeaderView::showSection(int logicalIndex)
979 Shows the section specified by \a logicalIndex.
980
981 \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
982 setSectionHidden()
983*/
984
985/*!
986 Returns \c true if the section specified by \a logicalIndex is explicitly
987 hidden from the user; otherwise returns \c false.
988
989 \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
990*/
991
992bool QHeaderView::isSectionHidden(int logicalIndex) const
993{
994 Q_D(const QHeaderView);
995 d->executePostedLayout();
996 if (d->hiddenSectionSize.isEmpty() || logicalIndex < 0 || logicalIndex >= d->sectionCount())
997 return false;
998 int visual = visualIndex(logicalIndex);
999 Q_ASSERT(visual != -1);
1000 return d->isVisualIndexHidden(visual);
1001}
1002
1003/*!
1004 \since 4.1
1005
1006 Returns the number of sections in the header that has been hidden.
1007
1008 \sa setSectionHidden(), isSectionHidden()
1009*/
1010int QHeaderView::hiddenSectionCount() const
1011{
1012 Q_D(const QHeaderView);
1013 return d->hiddenSectionSize.count();
1014}
1015
1016/*!
1017 If \a hide is true the section specified by \a logicalIndex is hidden;
1018 otherwise the section is shown.
1019
1020 \sa isSectionHidden(), hiddenSectionCount()
1021*/
1022
1023void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
1024{
1025 Q_D(QHeaderView);
1026 if (logicalIndex < 0 || logicalIndex >= count())
1027 return;
1028
1029 d->executePostedLayout();
1030 int visual = visualIndex(logicalIndex);
1031 Q_ASSERT(visual != -1);
1032 if (hide == d->isVisualIndexHidden(visual))
1033 return;
1034 if (hide) {
1035 const bool isHidingLastSection = (stretchLastSection() && logicalIndex == d->lastSectionLogicalIdx);
1036 if (isHidingLastSection)
1037 d->restoreSizeOnPrevLastSection(); // Restore here/now to get the right restore size.
1038 int size = d->headerSectionSize(visual);
1039 if (!d->hasAutoResizeSections())
1040 resizeSection(logicalIndex, 0);
1041 d->hiddenSectionSize.insert(logicalIndex, size);
1042 d->setVisualIndexHidden(visual, true);
1043 if (isHidingLastSection)
1044 d->setNewLastSection(d->lastVisibleVisualIndex());
1045 if (d->hasAutoResizeSections())
1046 d->doDelayedResizeSections();
1047 } else {
1048 int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
1049 d->hiddenSectionSize.remove(logicalIndex);
1050 d->setVisualIndexHidden(visual, false);
1051 resizeSection(logicalIndex, size);
1052
1053 const bool newLastSection = (stretchLastSection() && visual > visualIndex(d->lastSectionLogicalIdx));
1054 if (newLastSection) {
1055 d->restoreSizeOnPrevLastSection();
1056 d->setNewLastSection(visual);
1057 }
1058 }
1059}
1060
1061/*!
1062 Returns the number of sections in the header.
1063
1064 \sa sectionCountChanged(), length()
1065*/
1066
1067int QHeaderView::count() const
1068{
1069 Q_D(const QHeaderView);
1070 //Q_ASSERT(d->sectionCount == d->headerSectionCount());
1071 // ### this may affect the lazy layout
1072 d->executePostedLayout();
1073 return d->sectionCount();
1074}
1075
1076/*!
1077 Returns the visual index position of the section specified by the given
1078 \a logicalIndex, or -1 otherwise.
1079
1080 Hidden sections still have valid visual indexes.
1081
1082 \sa logicalIndex()
1083*/
1084
1085int QHeaderView::visualIndex(int logicalIndex) const
1086{
1087 Q_D(const QHeaderView);
1088 if (logicalIndex < 0)
1089 return -1;
1090 d->executePostedLayout();
1091 if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
1092 if (logicalIndex < d->sectionCount())
1093 return logicalIndex;
1094 } else if (logicalIndex < d->visualIndices.count()) {
1095 int visual = d->visualIndices.at(logicalIndex);
1096 Q_ASSERT(visual < d->sectionCount());
1097 return visual;
1098 }
1099 return -1;
1100}
1101
1102/*!
1103 Returns the logicalIndex for the section at the given \a visualIndex
1104 position, or -1 if visualIndex < 0 or visualIndex >= QHeaderView::count().
1105
1106 Note that the visualIndex is not affected by hidden sections.
1107
1108 \sa visualIndex(), sectionPosition()
1109*/
1110
1111int QHeaderView::logicalIndex(int visualIndex) const
1112{
1113 Q_D(const QHeaderView);
1114 if (visualIndex < 0 || visualIndex >= d->sectionCount())
1115 return -1;
1116 return d->logicalIndex(visualIndex);
1117}
1118
1119/*!
1120 \since 5.0
1121
1122 If \a movable is true, the header sections may be moved by the user;
1123 otherwise they are fixed in place.
1124
1125 When used in combination with QTreeView, the first column is not
1126 movable (since it contains the tree structure), by default.
1127 You can make it movable with setFirstSectionMovable(true).
1128
1129 \sa sectionsMovable(), sectionMoved()
1130 \sa setFirstSectionMovable()
1131*/
1132
1133void QHeaderView::setSectionsMovable(bool movable)
1134{
1135 Q_D(QHeaderView);
1136 d->movableSections = movable;
1137}
1138
1139// ### Qt 6 - remove this obsolete function
1140/*!
1141 \obsolete
1142 \fn void QHeaderView::setMovable(bool movable)
1143
1144 Use setSectionsMovable instead.
1145
1146 \sa setSectionsMovable()
1147*/
1148
1149/*!
1150 \since 5.0
1151
1152 Returns \c true if the header can be moved by the user; otherwise returns
1153 false.
1154
1155 By default, sections are movable in QTreeView (except for the first one),
1156 and not movable in QTableView.
1157
1158 \sa setSectionsMovable()
1159*/
1160
1161bool QHeaderView::sectionsMovable() const
1162{
1163 Q_D(const QHeaderView);
1164 return d->movableSections;
1165}
1166
1167// ### Qt 6 - remove this obsolete function
1168/*!
1169 \obsolete
1170 \fn bool QHeaderView::isMovable() const
1171
1172 Use sectionsMovable instead.
1173
1174 \sa sectionsMovable()
1175*/
1176
1177/*!
1178 \property QHeaderView::firstSectionMovable
1179 \brief Whether the first column can be moved by the user
1180
1181 This property controls whether the first column can be moved by the user.
1182 In a QTreeView, the first column holds the tree structure and is
1183 therefore non-movable by default, even after setSectionsMovable(true).
1184
1185 It can be made movable again, for instance in the case of flat lists
1186 without a tree structure, by calling this method.
1187 In such a scenario, it is recommended to call QTreeView::setRootIsDecorated(false)
1188 as well.
1189
1190 Setting it to true has no effect unless setSectionsMovable(true) is called
1191 as well.
1192
1193 \sa setSectionsMovable()
1194 \since 5.11
1195*/
1196void QHeaderView::setFirstSectionMovable(bool movable)
1197{
1198 Q_D(QHeaderView);
1199 d->allowUserMoveOfSection0 = movable;
1200}
1201
1202bool QHeaderView::isFirstSectionMovable() const
1203{
1204 Q_D(const QHeaderView);
1205 return d->allowUserMoveOfSection0;
1206}
1207
1208/*!
1209 \since 5.0
1210
1211 If \a clickable is true, the header will respond to single clicks.
1212
1213 \sa sectionsClickable(), sectionClicked(), sectionPressed(),
1214 setSortIndicatorShown()
1215*/
1216
1217void QHeaderView::setSectionsClickable(bool clickable)
1218{
1219 Q_D(QHeaderView);
1220 d->clickableSections = clickable;
1221}
1222
1223// ### Qt 6 - remove this obsolete function
1224/*!
1225 \obsolete
1226 \fn void QHeaderView::setClickable(bool clickable)
1227
1228 Use setSectionsClickable instead.
1229
1230 \sa setSectionsClickable()
1231*/
1232
1233/*!
1234 \since 5.0
1235
1236 Returns \c true if the header is clickable; otherwise returns \c false. A
1237 clickable header could be set up to allow the user to change the
1238 representation of the data in the view related to the header.
1239
1240 \sa setSectionsClickable()
1241*/
1242
1243bool QHeaderView::sectionsClickable() const
1244{
1245 Q_D(const QHeaderView);
1246 return d->clickableSections;
1247}
1248
1249// ### Qt 6 - remove this obsolete function
1250/*!
1251 \obsolete
1252 \fn bool QHeaderView::isClickable() const
1253
1254 Use sectionsClickable instead.
1255
1256 \sa sectionsClickable()
1257*/
1258
1259void QHeaderView::setHighlightSections(bool highlight)
1260{
1261 Q_D(QHeaderView);
1262 d->highlightSelected = highlight;
1263}
1264
1265bool QHeaderView::highlightSections() const
1266{
1267 Q_D(const QHeaderView);
1268 return d->highlightSelected;
1269}
1270
1271/*!
1272 \since 5.0
1273
1274 Sets the constraints on how the header can be resized to those described
1275 by the given \a mode.
1276
1277 \sa resizeMode(), length(), sectionResized()
1278*/
1279
1280void QHeaderView::setSectionResizeMode(ResizeMode mode)
1281{
1282 Q_D(QHeaderView);
1283 initializeSections();
1284 d->stretchSections = (mode == Stretch ? count() : 0);
1285 d->contentsSections = (mode == ResizeToContents ? count() : 0);
1286 d->setGlobalHeaderResizeMode(mode);
1287 if (d->hasAutoResizeSections())
1288 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1289}
1290
1291/*!
1292 \since 5.0
1293
1294 Sets the constraints on how the section specified by \a logicalIndex in
1295 the header can be resized to those described by the given \a mode. The logical
1296 index should exist at the time this function is called.
1297
1298 \note This setting will be ignored for the last section if the stretchLastSection
1299 property is set to true. This is the default for the horizontal headers provided
1300 by QTreeView.
1301
1302 \sa setStretchLastSection(), resizeContentsPrecision()
1303*/
1304
1305void QHeaderView::setSectionResizeMode(int logicalIndex, ResizeMode mode)
1306{
1307 Q_D(QHeaderView);
1308 int visual = visualIndex(logicalIndex);
1309 Q_ASSERT(visual != -1);
1310
1311 ResizeMode old = d->headerSectionResizeMode(visual);
1312 d->setHeaderSectionResizeMode(visual, mode);
1313
1314 if (mode == Stretch && old != Stretch)
1315 ++d->stretchSections;
1316 else if (mode == ResizeToContents && old != ResizeToContents)
1317 ++d->contentsSections;
1318 else if (mode != Stretch && old == Stretch)
1319 --d->stretchSections;
1320 else if (mode != ResizeToContents && old == ResizeToContents)
1321 --d->contentsSections;
1322
1323 if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
1324 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1325}
1326
1327// ### Qt 6 - remove this obsolete function
1328/*!
1329 \overload
1330 \obsolete
1331 \fn void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
1332
1333 Use setSectionResizeMode instead.
1334
1335 \sa setSectionResizeMode()
1336*/
1337
1338/*!
1339 \obsolete
1340 \fn void QHeaderView::setResizeMode(ResizeMode mode)
1341
1342 Use setSectionResizeMode instead.
1343
1344 \sa setSectionResizeMode()
1345*/
1346
1347/*!
1348 \since 5.0
1349
1350 Returns the resize mode that applies to the section specified by the given
1351 \a logicalIndex.
1352
1353 \sa setSectionResizeMode()
1354*/
1355
1356QHeaderView::ResizeMode QHeaderView::sectionResizeMode(int logicalIndex) const
1357{
1358 Q_D(const QHeaderView);
1359 int visual = visualIndex(logicalIndex);
1360 if (visual == -1)
1361 return Fixed; //the default value
1362 return d->headerSectionResizeMode(visual);
1363}
1364
1365/*!
1366 \since 5.2
1367 Sets how precise QHeaderView should calculate the size when ResizeToContents is used.
1368 A low value will provide a less accurate but fast auto resize while a higher
1369 value will provide a more accurate resize that however can be slow.
1370
1371 The number \a precision specifies how many sections that should be consider
1372 when calculating the preferred size.
1373
1374 The default value is 1000 meaning that a horizontal column with auto-resize will look
1375 at maximum 1000 rows on calculating when doing an auto resize.
1376
1377 Special value 0 means that it will look at only the visible area.
1378 Special value -1 will imply looking at all elements.
1379
1380 This value is used in QTableView::sizeHintForColumn(), QTableView::sizeHintForRow()
1381 and QTreeView::sizeHintForColumn(). Reimplementing these functions can make this
1382 function not having an effect.
1383
1384 \sa resizeContentsPrecision(), setSectionResizeMode(), resizeSections(), QTableView::sizeHintForColumn(), QTableView::sizeHintForRow(), QTreeView::sizeHintForColumn()
1385*/
1386
1387void QHeaderView::setResizeContentsPrecision(int precision)
1388{
1389 Q_D(QHeaderView);
1390 d->resizeContentsPrecision = precision;
1391}
1392
1393/*!
1394 \since 5.2
1395 Returns how precise QHeaderView will calculate on ResizeToContents.
1396
1397 \sa setResizeContentsPrecision(), setSectionResizeMode()
1398
1399*/
1400
1401int QHeaderView::resizeContentsPrecision() const
1402{
1403 Q_D(const QHeaderView);
1404 return d->resizeContentsPrecision;
1405}
1406
1407// ### Qt 6 - remove this obsolete function
1408/*!
1409 \obsolete
1410 \fn QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
1411
1412 Use sectionResizeMode instead.
1413
1414 \sa sectionResizeMode()
1415*/
1416
1417/*!
1418 \since 4.1
1419
1420 Returns the number of sections that are set to resize mode stretch. In
1421 views, this can be used to see if the headerview needs to resize the
1422 sections when the view's geometry changes.
1423
1424 \sa stretchLastSection, resizeMode()
1425*/
1426
1427int QHeaderView::stretchSectionCount() const
1428{
1429 Q_D(const QHeaderView);
1430 return d->stretchSections;
1431}
1432
1433/*!
1434 \property QHeaderView::showSortIndicator
1435 \brief whether the sort indicator is shown
1436
1437 By default, this property is \c false.
1438
1439 \sa setSectionsClickable()
1440*/
1441
1442void QHeaderView::setSortIndicatorShown(bool show)
1443{
1444 Q_D(QHeaderView);
1445 if (d->sortIndicatorShown == show)
1446 return;
1447
1448 d->sortIndicatorShown = show;
1449
1450 if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
1451 return;
1452
1453 if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents)
1454 resizeSections();
1455
1456 d->viewport->update();
1457}
1458
1459bool QHeaderView::isSortIndicatorShown() const
1460{
1461 Q_D(const QHeaderView);
1462 return d->sortIndicatorShown;
1463}
1464
1465/*!
1466 Sets the sort indicator for the section specified by the given
1467 \a logicalIndex in the direction specified by \a order, and removes the
1468 sort indicator from any other section that was showing it.
1469
1470 \a logicalIndex may be -1, in which case no sort indicator will be shown
1471 and the model will return to its natural, unsorted order. Note that not
1472 all models support this and may even crash in this case.
1473
1474 \sa sortIndicatorSection(), sortIndicatorOrder()
1475*/
1476
1477void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
1478{
1479 Q_D(QHeaderView);
1480
1481 // This is so that people can set the position of the sort indicator before the fill the model
1482 int old = d->sortIndicatorSection;
1483 if (old == logicalIndex && order == d->sortIndicatorOrder)
1484 return;
1485 d->sortIndicatorSection = logicalIndex;
1486 d->sortIndicatorOrder = order;
1487
1488 if (logicalIndex >= d->sectionCount()) {
1489 emit sortIndicatorChanged(logicalIndex, order);
1490 return; // nothing to do
1491 }
1492
1493 if (old != logicalIndex
1494 && ((logicalIndex >= 0 && sectionResizeMode(logicalIndex) == ResizeToContents)
1495 || old >= d->sectionCount() || (old >= 0 && sectionResizeMode(old) == ResizeToContents))) {
1496 resizeSections();
1497 d->viewport->update();
1498 } else {
1499 if (old >= 0 && old != logicalIndex)
1500 updateSection(old);
1501 if (logicalIndex >= 0)
1502 updateSection(logicalIndex);
1503 }
1504
1505 emit sortIndicatorChanged(logicalIndex, order);
1506}
1507
1508/*!
1509 Returns the logical index of the section that has a sort indicator.
1510 By default this is section 0.
1511
1512 \sa setSortIndicator(), sortIndicatorOrder(), setSortIndicatorShown()
1513*/
1514
1515int QHeaderView::sortIndicatorSection() const
1516{
1517 Q_D(const QHeaderView);
1518 return d->sortIndicatorSection;
1519}
1520
1521/*!
1522 Returns the order for the sort indicator. If no section has a sort
1523 indicator the return value of this function is undefined.
1524
1525 \sa setSortIndicator(), sortIndicatorSection()
1526*/
1527
1528Qt::SortOrder QHeaderView::sortIndicatorOrder() const
1529{
1530 Q_D(const QHeaderView);
1531 return d->sortIndicatorOrder;
1532}
1533
1534/*!
1535 \property QHeaderView::stretchLastSection
1536 \brief whether the last visible section in the header takes up all the
1537 available space
1538
1539 The default value is false.
1540
1541 \note The horizontal headers provided by QTreeView are configured with this
1542 property set to true, ensuring that the view does not waste any of the
1543 space assigned to it for its header. If this value is set to true, this
1544 property will override the resize mode set on the last section in the
1545 header.
1546
1547 \sa setSectionResizeMode()
1548*/
1549bool QHeaderView::stretchLastSection() const
1550{
1551 Q_D(const QHeaderView);
1552 return d->stretchLastSection;
1553}
1554
1555void QHeaderView::setStretchLastSection(bool stretch)
1556{
1557 Q_D(QHeaderView);
1558 if (d->stretchLastSection == stretch)
1559 return;
1560 d->stretchLastSection = stretch;
1561 if (d->state != QHeaderViewPrivate::NoState)
1562 return;
1563 if (stretch) {
1564 d->setNewLastSection(d->lastVisibleVisualIndex());
1565 resizeSections();
1566 } else {
1567 d->restoreSizeOnPrevLastSection();
1568 }
1569}
1570
1571/*!
1572 \since 4.2
1573 \property QHeaderView::cascadingSectionResizes
1574 \brief whether interactive resizing will be cascaded to the following
1575 sections once the section being resized by the user has reached its
1576 minimum size
1577
1578 This property only affects sections that have \l Interactive as their
1579 resize mode.
1580
1581 The default value is false.
1582
1583 \sa setSectionResizeMode()
1584*/
1585bool QHeaderView::cascadingSectionResizes() const
1586{
1587 Q_D(const QHeaderView);
1588 return d->cascadingResizing;
1589}
1590
1591void QHeaderView::setCascadingSectionResizes(bool enable)
1592{
1593 Q_D(QHeaderView);
1594 d->cascadingResizing = enable;
1595}
1596
1597/*!
1598 \property QHeaderView::defaultSectionSize
1599 \brief the default size of the header sections before resizing.
1600
1601 This property only affects sections that have \l Interactive or \l Fixed
1602 as their resize mode.
1603
1604 By default, the value of this property is style dependent.
1605 Thus, when the style changes, this property updates from it.
1606 Calling setDefaultSectionSize() stops the updates, calling
1607 resetDefaultSectionSize() will restore default behavior.
1608
1609 \sa setSectionResizeMode(), minimumSectionSize
1610*/
1611int QHeaderView::defaultSectionSize() const
1612{
1613 Q_D(const QHeaderView);
1614 return d->defaultSectionSize;
1615}
1616
1617void QHeaderView::setDefaultSectionSize(int size)
1618{
1619 Q_D(QHeaderView);
1620 if (size < 0 || size > maxSizeSection)
1621 return;
1622 d->setDefaultSectionSize(size);
1623}
1624
1625void QHeaderView::resetDefaultSectionSize()
1626{
1627 Q_D(QHeaderView);
1628 if (d->customDefaultSectionSize) {
1629 d->updateDefaultSectionSizeFromStyle();
1630 d->customDefaultSectionSize = false;
1631 }
1632}
1633
1634/*!
1635 \since 4.2
1636 \property QHeaderView::minimumSectionSize
1637 \brief the minimum size of the header sections.
1638
1639 The minimum section size is the smallest section size allowed. If the
1640 minimum section size is set to -1, QHeaderView will use the maximum of
1641 the \l{QApplication::globalStrut()}{global strut} or the
1642 \l{fontMetrics()}{font metrics} size.
1643
1644 This property is honored by all \l{ResizeMode}{resize modes}.
1645
1646 \sa setSectionResizeMode(), defaultSectionSize
1647*/
1648int QHeaderView::minimumSectionSize() const
1649{
1650 Q_D(const QHeaderView);
1651 if (d->minimumSectionSize == -1) {
1652 QSize strut = QApplication::globalStrut();
1653 int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
1654 if (d->orientation == Qt::Horizontal)
1655 return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
1656 return qMax(strut.height(), (fontMetrics().height() + margin));
1657 }
1658 return d->minimumSectionSize;
1659}
1660
1661void QHeaderView::setMinimumSectionSize(int size)
1662{
1663 Q_D(QHeaderView);
1664 if (size < -1 || size > maxSizeSection)
1665 return;
1666 // larger new min size - check current section sizes
1667 const bool needSizeCheck = size > d->minimumSectionSize;
1668 d->minimumSectionSize = size;
1669 if (d->minimumSectionSize > maximumSectionSize())
1670 setMaximumSectionSize(size);
1671
1672 if (needSizeCheck) {
1673 if (d->hasAutoResizeSections()) {
1674 d->doDelayedResizeSections();
1675 } else {
1676 for (int visual = 0; visual < d->sectionCount(); ++visual) {
1677 if (d->isVisualIndexHidden(visual))
1678 continue;
1679 if (d->headerSectionSize(visual) < d->minimumSectionSize)
1680 resizeSection(logicalIndex(visual), size);
1681 }
1682 }
1683 }
1684
1685}
1686
1687/*!
1688 \since 5.2
1689 \property QHeaderView::maximumSectionSize
1690 \brief the maximum size of the header sections.
1691
1692 The maximum section size is the largest section size allowed.
1693 The default value for this property is 1048575, which is also the largest
1694 possible size for a section. Setting maximum to -1 will reset the value to
1695 the largest section size.
1696
1697 With exception of stretch this property is honored by all \l{ResizeMode}{resize modes}
1698
1699 \sa setSectionResizeMode(), defaultSectionSize
1700*/
1701int QHeaderView::maximumSectionSize() const
1702{
1703 Q_D(const QHeaderView);
1704 if (d->maximumSectionSize == -1)
1705 return maxSizeSection;
1706 return d->maximumSectionSize;
1707}
1708
1709void QHeaderView::setMaximumSectionSize(int size)
1710{
1711 Q_D(QHeaderView);
1712 if (size == -1) {
1713 d->maximumSectionSize = maxSizeSection;
1714 return;
1715 }
1716 if (size < 0 || size > maxSizeSection)
1717 return;
1718 if (minimumSectionSize() > size)
1719 d->minimumSectionSize = size;
1720
1721 // smaller new max size - check current section sizes
1722 const bool needSizeCheck = size < d->maximumSectionSize;
1723 d->maximumSectionSize = size;
1724
1725 if (needSizeCheck) {
1726 if (d->hasAutoResizeSections()) {
1727 d->doDelayedResizeSections();
1728 } else {
1729 for (int visual = 0; visual < d->sectionCount(); ++visual) {
1730 if (d->isVisualIndexHidden(visual))
1731 continue;
1732 if (d->headerSectionSize(visual) > d->maximumSectionSize)
1733 resizeSection(logicalIndex(visual), size);
1734 }
1735 }
1736 }
1737}
1738
1739
1740/*!
1741 \since 4.1
1742 \property QHeaderView::defaultAlignment
1743 \brief the default alignment of the text in each header section
1744*/
1745
1746Qt::Alignment QHeaderView::defaultAlignment() const
1747{
1748 Q_D(const QHeaderView);
1749 return d->defaultAlignment;
1750}
1751
1752void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
1753{
1754 Q_D(QHeaderView);
1755 if (d->defaultAlignment == alignment)
1756 return;
1757
1758 d->defaultAlignment = alignment;
1759 d->viewport->update();
1760}
1761
1762/*!
1763 \internal
1764*/
1765void QHeaderView::doItemsLayout()
1766{
1767 initializeSections();
1768 QAbstractItemView::doItemsLayout();
1769}
1770
1771/*!
1772 Returns \c true if sections in the header has been moved; otherwise returns
1773 false;
1774
1775 \sa moveSection()
1776*/
1777bool QHeaderView::sectionsMoved() const
1778{
1779 Q_D(const QHeaderView);
1780 return !d->visualIndices.isEmpty();
1781}
1782
1783/*!
1784 \since 4.1
1785
1786 Returns \c true if sections in the header has been hidden; otherwise returns
1787 false;
1788
1789 \sa setSectionHidden()
1790*/
1791bool QHeaderView::sectionsHidden() const
1792{
1793 Q_D(const QHeaderView);
1794 return !d->hiddenSectionSize.isEmpty();
1795}
1796
1797#ifndef QT_NO_DATASTREAM
1798/*!
1799 \since 4.3
1800
1801 Saves the current state of this header view.
1802
1803 To restore the saved state, pass the return value to restoreState().
1804
1805 \sa restoreState()
1806*/
1807QByteArray QHeaderView::saveState() const
1808{
1809 Q_D(const QHeaderView);
1810 QByteArray data;
1811 QDataStream stream(&data, QIODevice::WriteOnly);
1812 stream << QHeaderViewPrivate::VersionMarker;
1813 stream << 0; // current version is 0
1814 d->write(stream);
1815 return data;
1816}
1817
1818/*!
1819 \since 4.3
1820 Restores the \a state of this header view.
1821 This function returns \c true if the state was restored; otherwise returns
1822 false.
1823
1824 \sa saveState()
1825*/
1826bool QHeaderView::restoreState(const QByteArray &state)
1827{
1828 Q_D(QHeaderView);
1829 if (state.isEmpty())
1830 return false;
1831 QByteArray data = state;
1832 QDataStream stream(&data, QIODevice::ReadOnly);
1833 int marker;
1834 int ver;
1835 stream >> marker;
1836 stream >> ver;
1837 if (stream.status() != QDataStream::Ok
1838 || marker != QHeaderViewPrivate::VersionMarker
1839 || ver != 0) // current version is 0
1840 return false;
1841
1842 if (d->read(stream)) {
1843 emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
1844 d->viewport->update();
1845 return true;
1846 }
1847 return false;
1848}
1849#endif // QT_NO_DATASTREAM
1850
1851/*!
1852 \reimp
1853*/
1854void QHeaderView::reset()
1855{
1856 Q_D(QHeaderView);
1857 QAbstractItemView::reset();
1858 // it would be correct to call clear, but some apps rely
1859 // on the header keeping the sections, even after calling reset
1860 //d->clear();
1861 initializeSections();
1862 d->invalidateCachedSizeHint();
1863}
1864
1865/*!
1866 Updates the changed header sections with the given \a orientation, from
1867 \a logicalFirst to \a logicalLast inclusive.
1868*/
1869void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
1870{
1871 Q_D(QHeaderView);
1872 if (d->orientation != orientation)
1873 return;
1874
1875 if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
1876 return;
1877
1878 d->invalidateCachedSizeHint();
1879
1880 int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
1881
1882 for (int section = logicalFirst; section <= logicalLast; ++section) {
1883 const int visual = visualIndex(section);
1884 firstVisualIndex = qMin(firstVisualIndex, visual);
1885 lastVisualIndex = qMax(lastVisualIndex, visual);
1886 }
1887
1888 d->executePostedResize();
1889 const int first = d->headerSectionPosition(firstVisualIndex),
1890 last = d->headerSectionPosition(lastVisualIndex)
1891 + d->headerSectionSize(lastVisualIndex);
1892
1893 if (orientation == Qt::Horizontal) {
1894 d->viewport->update(first, 0, last - first, d->viewport->height());
1895 } else {
1896 d->viewport->update(0, first, d->viewport->width(), last - first);
1897 }
1898}
1899
1900/*!
1901 \internal
1902 \since 4.2
1903
1904 Updates the section specified by the given \a logicalIndex.
1905*/
1906
1907void QHeaderView::updateSection(int logicalIndex)
1908{
1909 Q_D(QHeaderView);
1910 if (d->orientation == Qt::Horizontal)
1911 d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
1912 0, sectionSize(logicalIndex), d->viewport->height()));
1913 else
1914 d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
1915 d->viewport->width(), sectionSize(logicalIndex)));
1916}
1917
1918/*!
1919 Resizes the sections according to their size hints. Normally, you do not
1920 have to call this function.
1921*/
1922
1923void QHeaderView::resizeSections()
1924{
1925 Q_D(QHeaderView);
1926 if (d->hasAutoResizeSections())
1927 d->resizeSections(Interactive, false); // no global resize mode
1928}
1929
1930/*!
1931 This slot is called when sections are inserted into the \a parent.
1932 \a logicalFirst and \a logicalLast indices signify where the new sections
1933 were inserted.
1934
1935 If only one section is inserted, \a logicalFirst and \a logicalLast will
1936 be the same.
1937*/
1938
1939void QHeaderView::sectionsInserted(const QModelIndex &parent,
1940 int logicalFirst, int logicalLast)
1941{
1942 Q_D(QHeaderView);
1943 if (parent != d->root)
1944 return; // we only handle changes in the root level
1945 int oldCount = d->sectionCount();
1946
1947 d->invalidateCachedSizeHint();
1948
1949 if (d->state == QHeaderViewPrivate::ResizeSection)
1950 d->preventCursorChangeInSetOffset = true;
1951
1952 // add the new sections
1953 int insertAt = logicalFirst;
1954 int insertCount = logicalLast - logicalFirst + 1;
1955
1956 bool lastSectionActualChange = false;
1957 if (stretchLastSection()) {
1958
1959 int visualIndexForStretch = d->lastSectionLogicalIdx;
1960 if (d->lastSectionLogicalIdx >= 0 && d->lastSectionLogicalIdx < d->visualIndices.size())
1961 visualIndexForStretch = d->visualIndices[d->lastSectionLogicalIdx]; // We cannot call visualIndex since it executes executePostedLayout()
1962 // and it is likely to bypass initializeSections() and we may end up here again. Doing the insert twice.
1963
1964 if (d->lastSectionLogicalIdx < 0 || insertAt >= visualIndexForStretch)
1965 lastSectionActualChange = true;
1966
1967 if (d->lastSectionLogicalIdx >= logicalFirst)
1968 d->lastSectionLogicalIdx += insertCount; // We do not want to emit resize before we have fixed the count
1969 }
1970
1971 QHeaderViewPrivate::SectionItem section(d->defaultSectionSize, d->globalResizeMode);
1972 d->sectionStartposRecalc = true;
1973
1974 if (d->sectionItems.isEmpty() || insertAt >= d->sectionItems.count()) {
1975 int insertLength = d->defaultSectionSize * insertCount;
1976 d->length += insertLength;
1977 d->sectionItems.insert(d->sectionItems.count(), insertCount, section); // append
1978 } else {
1979 // separate them out into their own sections
1980 int insertLength = d->defaultSectionSize * insertCount;
1981 d->length += insertLength;
1982 d->sectionItems.insert(insertAt, insertCount, section);
1983 }
1984
1985 // update sorting column
1986 if (d->sortIndicatorSection >= logicalFirst)
1987 d->sortIndicatorSection += insertCount;
1988
1989 // update resize mode section counts
1990 if (d->globalResizeMode == Stretch)
1991 d->stretchSections = d->sectionCount();
1992 else if (d->globalResizeMode == ResizeToContents)
1993 d->contentsSections = d->sectionCount();
1994
1995 // clear selection cache
1996 d->sectionSelected.clear();
1997
1998 // update mapping
1999 if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
2000 Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
2001 int mappingCount = d->visualIndices.count();
2002 for (int i = 0; i < mappingCount; ++i) {
2003 if (d->visualIndices.at(i) >= logicalFirst)
2004 d->visualIndices[i] += insertCount;
2005 if (d->logicalIndices.at(i) >= logicalFirst)
2006 d->logicalIndices[i] += insertCount;
2007 }
2008 for (int j = logicalFirst; j <= logicalLast; ++j) {
2009 d->visualIndices.insert(j, j);
2010 d->logicalIndices.insert(j, j);
2011 }
2012 }
2013
2014 // insert sections into hiddenSectionSize
2015 QHash<int, int> newHiddenSectionSize; // from logical index to section size
2016 for (QHash<int, int>::const_iterator it = d->hiddenSectionSize.cbegin(),
2017 end = d->hiddenSectionSize.cend(); it != end; ++it) {
2018 const int oldIndex = it.key();
2019 const int newIndex = (oldIndex < logicalFirst) ? oldIndex : oldIndex + insertCount;
2020 newHiddenSectionSize[newIndex] = it.value();
2021 }
2022 d->hiddenSectionSize.swap(newHiddenSectionSize);
2023
2024 d->doDelayedResizeSections();
2025 emit sectionCountChanged(oldCount, count());
2026
2027 if (lastSectionActualChange)
2028 d->maybeRestorePrevLastSectionAndStretchLast();
2029
2030 // if the new sections were not updated by resizing, we need to update now
2031 if (!d->hasAutoResizeSections())
2032 d->viewport->update();
2033}
2034
2035/*!
2036 This slot is called when sections are removed from the \a parent.
2037 \a logicalFirst and \a logicalLast signify where the sections were removed.
2038
2039 If only one section is removed, \a logicalFirst and \a logicalLast will
2040 be the same.
2041*/
2042
2043void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
2044 int logicalFirst, int logicalLast)
2045{
2046 Q_UNUSED(parent);
2047 Q_UNUSED(logicalFirst);
2048 Q_UNUSED(logicalLast);
2049}
2050
2051void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
2052{
2053 Q_Q(QHeaderView);
2054 const int changeCount = logicalLast - logicalFirst + 1;
2055
2056 // remove sections from hiddenSectionSize
2057 QHash<int, int> newHiddenSectionSize; // from logical index to section size
2058 for (int i = 0; i < logicalFirst; ++i)
2059 if (q->isSectionHidden(i))
2060 newHiddenSectionSize[i] = hiddenSectionSize[i];
2061 for (int j = logicalLast + 1; j < sectionCount(); ++j)
2062 if (q->isSectionHidden(j))
2063 newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
2064 hiddenSectionSize = newHiddenSectionSize;
2065}
2066
2067void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
2068 int logicalFirst, int logicalLast)
2069{
2070 Q_Q(QHeaderView);
2071 if (parent != root)
2072 return; // we only handle changes in the root level
2073 if (qMin(logicalFirst, logicalLast) < 0
2074 || qMax(logicalLast, logicalFirst) >= sectionCount())
2075 return;
2076 int oldCount = q->count();
2077 int changeCount = logicalLast - logicalFirst + 1;
2078
2079 if (state == QHeaderViewPrivate::ResizeSection)
2080 preventCursorChangeInSetOffset = true;
2081
2082 updateHiddenSections(logicalFirst, logicalLast);
2083
2084 if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
2085 //Q_ASSERT(headerSectionCount() == sectionCount);
2086 removeSectionsFromSectionItems(logicalFirst, logicalLast);
2087 } else {
2088 if (logicalFirst == logicalLast) { // Remove just one index.
2089 int l = logicalFirst;
2090 int visual = visualIndices.at(l);
2091 Q_ASSERT(sectionCount() == logicalIndices.count());
2092 for (int v = 0; v < sectionCount(); ++v) {
2093 if (v > visual) {
2094 int logical = logicalIndices.at(v);
2095 --(visualIndices[logical]);
2096 }
2097 if (logicalIndex(v) > l) // no need to move the positions before l
2098 --(logicalIndices[v]);
2099 }
2100 logicalIndices.remove(visual);
2101 visualIndices.remove(l);
2102 //Q_ASSERT(headerSectionCount() == sectionCount);
2103 removeSectionsFromSectionItems(visual, visual);
2104 } else {
2105 sectionStartposRecalc = true; // We will need to recalc positions after removing items
2106 for (int u = 0; u < sectionItems.count(); ++u) // Store section info
2107 sectionItems.at(u).tmpLogIdx = logicalIndices.at(u);
2108 for (int v = sectionItems.count() - 1; v >= 0; --v) { // Remove the sections
2109 if (logicalFirst <= sectionItems.at(v).tmpLogIdx && sectionItems.at(v).tmpLogIdx <= logicalLast)
2110 removeSectionsFromSectionItems(v, v);
2111 }
2112 visualIndices.resize(sectionItems.count());
2113 logicalIndices.resize(sectionItems.count());
2114 int* visual_data = visualIndices.data();
2115 int* logical_data = logicalIndices.data();
2116 for (int w = 0; w < sectionItems.count(); ++w) { // Restore visual and logical indexes
2117 int logindex = sectionItems.at(w).tmpLogIdx;
2118 if (logindex > logicalFirst)
2119 logindex -= changeCount;
2120 visual_data[logindex] = w;
2121 logical_data[w] = logindex;
2122 }
2123 }
2124 // ### handle sectionSelection (sectionHidden is handled by updateHiddenSections)
2125 }
2126
2127 // update sorting column
2128 if (sortIndicatorSection >= logicalFirst) {
2129 if (sortIndicatorSection <= logicalLast)
2130 sortIndicatorSection = -1;
2131 else
2132 sortIndicatorSection -= changeCount;
2133 }
2134
2135 // if we only have the last section (the "end" position) left, the header is empty
2136 if (sectionCount() <= 0)
2137 clear();
2138 invalidateCachedSizeHint();
2139 emit q->sectionCountChanged(oldCount, q->count());
2140
2141 if (q->stretchLastSection()) {
2142 const bool lastSectionRemoved = lastSectionLogicalIdx >= logicalFirst && lastSectionLogicalIdx <= logicalLast;
2143 if (lastSectionRemoved)
2144 setNewLastSection(lastVisibleVisualIndex());
2145 else
2146 lastSectionLogicalIdx = logicalIndex(lastVisibleVisualIndex()); // Just update the last log index.
2147 doDelayedResizeSections();
2148 }
2149
2150 viewport->update();
2151}
2152
2153void QHeaderViewPrivate::_q_sectionsAboutToBeMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2154{
2155 if (sourceParent != root || destinationParent != root)
2156 return; // we only handle changes in the root level
2157 Q_UNUSED(logicalStart);
2158 Q_UNUSED(logicalEnd);
2159 Q_UNUSED(logicalDestination);
2160 _q_sectionsAboutToBeChanged();
2161}
2162
2163void QHeaderViewPrivate::_q_sectionsMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination)
2164{
2165 if (sourceParent != root || destinationParent != root)
2166 return; // we only handle changes in the root level
2167 Q_UNUSED(logicalStart);
2168 Q_UNUSED(logicalEnd);
2169 Q_UNUSED(logicalDestination);
2170 _q_sectionsChanged();
2171}
2172
2173void QHeaderViewPrivate::_q_sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &,
2174 QAbstractItemModel::LayoutChangeHint hint)
2175{
2176 if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2177 (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2178 return;
2179
2180 //if there is no row/column we can't have mapping for columns
2181 //because no QModelIndex in the model would be valid
2182 // ### this is far from being bullet-proof and we would need a real system to
2183 // ### map columns or rows persistently
2184 if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
2185 || model->columnCount(root) == 0)
2186 return;
2187
2188 layoutChangePersistentSections.clear();
2189 layoutChangePersistentSections.reserve(std::min(10, sectionItems.count()));
2190 // after layoutChanged another section can be last stretched section
2191 if (stretchLastSection && lastSectionLogicalIdx >= 0 && lastSectionLogicalIdx < sectionItems.count()) {
2192 const int visual = visualIndex(lastSectionLogicalIdx);
2193 if (visual >= 0 && visual < sectionItems.size()) {
2194 auto &itemRef = sectionItems[visual];
2195 if (itemRef.size != lastSectionSize) {
2196 length += lastSectionSize - itemRef.size;
2197 itemRef.size = lastSectionSize;
2198 }
2199 }
2200 }
2201 for (int i = 0; i < sectionItems.size(); ++i) {
2202 auto s = sectionItems.at(i);
2203 // only add if the section is not default and not visually moved
2204 if (s.size == defaultSectionSize && !s.isHidden && s.resizeMode == globalResizeMode)
2205 continue;
2206
2207 const int logical = logicalIndex(i);
2208 if (s.isHidden)
2209 s.size = hiddenSectionSize.value(logical);
2210
2211 // ### note that we are using column or row 0
2212 layoutChangePersistentSections.append({orientation == Qt::Horizontal
2213 ? model->index(0, logical, root)
2214 : model->index(logical, 0, root),
2215 s});
2216 }
2217}
2218
2219void QHeaderViewPrivate::_q_sectionsChanged(const QList<QPersistentModelIndex> &,
2220 QAbstractItemModel::LayoutChangeHint hint)
2221{
2222 if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) ||
2223 (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical))
2224 return;
2225
2226 Q_Q(QHeaderView);
2227 viewport->update();
2228
2229 const auto oldPersistentSections = layoutChangePersistentSections;
2230 layoutChangePersistentSections.clear();
2231
2232 const int newCount = modelSectionCount();
2233 const int oldCount = sectionItems.size();
2234 if (newCount == 0) {
2235 clear();
2236 if (oldCount != 0)
2237 emit q->sectionCountChanged(oldCount, 0);
2238 return;
2239 }
2240
2241 bool hasPersistantIndexes = false;
2242 for (const auto &item : oldPersistentSections) {
2243 if (item.index.isValid()) {
2244 hasPersistantIndexes = true;
2245 break;
2246 }
2247 }
2248
2249 // Though far from perfect we here try to retain earlier/existing behavior
2250 // ### See QHeaderViewPrivate::_q_layoutAboutToBeChanged()
2251 // When we don't have valid hasPersistantIndexes it can be due to
2252 // - all sections are default sections
2253 // - the row/column 0 which is used for persistent indexes is gone
2254 // - all non-default sections were removed
2255 // case one is trivial, in case two we assume nothing else changed (it's the best
2256 // guess we can do - everything else can not be handled correctly for now)
2257 // case three can not be handled correctly with layoutChanged - removeSections
2258 // should be used instead for this
2259 if (!hasPersistantIndexes) {
2260 if (oldCount != newCount)
2261 q->initializeSections();
2262 return;
2263 }
2264
2265 // adjust section size
2266 if (newCount != oldCount) {
2267 const int min = qBound(0, oldCount, newCount - 1);
2268 q->initializeSections(min, newCount - 1);
2269 }
2270 // reset sections
2271 sectionItems.fill(SectionItem(defaultSectionSize, globalResizeMode), newCount);
2272
2273 // all hidden sections are in oldPersistentSections
2274 hiddenSectionSize.clear();
2275
2276 for (const auto &item : oldPersistentSections) {
2277 const auto &index = item.index;
2278 if (!index.isValid())
2279 continue;
2280
2281 const int newLogicalIndex = (orientation == Qt::Horizontal
2282 ? index.column()
2283 : index.row());
2284 // the new visualIndices are already adjusted / reset by initializeSections()
2285 const int newVisualIndex = visualIndex(newLogicalIndex);
2286 auto &newSection = sectionItems[newVisualIndex];
2287 newSection = item.section;
2288
2289 if (newSection.isHidden) {
2290 // otherwise setSectionHidden will return without doing anything
2291 newSection.isHidden = false;
2292 q->setSectionHidden(newLogicalIndex, true);
2293 }
2294 }
2295
2296 recalcSectionStartPos();
2297 length = headerLength();
2298
2299 if (stretchLastSection) {
2300 // force rebuild of stretched section later on
2301 lastSectionLogicalIdx = -1;
2302 maybeRestorePrevLastSectionAndStretchLast();
2303 }
2304}
2305
2306/*!
2307 \internal
2308*/
2309
2310void QHeaderView::initializeSections()
2311{
2312 Q_D(QHeaderView);
2313 const int oldCount = d->sectionCount();
2314 const int newCount = d->modelSectionCount();
2315 if (newCount <= 0) {
2316 d->clear();
2317 emit sectionCountChanged(oldCount, 0);
2318 } else if (newCount != oldCount) {
2319 const int min = qBound(0, oldCount, newCount - 1);
2320 initializeSections(min, newCount - 1);
2321 if (stretchLastSection()) // we've already gotten the size hint
2322 d->maybeRestorePrevLastSectionAndStretchLast();
2323
2324 // make sure we update the hidden sections
2325 // simulate remove from newCount to oldCount
2326 if (newCount < oldCount)
2327 d->updateHiddenSections(newCount, oldCount);
2328 }
2329}
2330
2331/*!
2332 \internal
2333*/
2334
2335void QHeaderView::initializeSections(int start, int end)
2336{
2337 Q_D(QHeaderView);
2338
2339 Q_ASSERT(start >= 0);
2340 Q_ASSERT(end >= 0);
2341
2342 d->invalidateCachedSizeHint();
2343 int oldCount = d->sectionCount();
2344
2345 if (end + 1 < d->sectionCount()) {
2346 int newCount = end + 1;
2347 d->removeSectionsFromSectionItems(newCount, d->sectionCount() - 1);
2348 if (!d->hiddenSectionSize.isEmpty()) {
2349 if (oldCount - newCount > d->hiddenSectionSize.count()) {
2350 for (int i = end + 1; i < d->sectionCount(); ++i)
2351 d->hiddenSectionSize.remove(i);
2352 } else {
2353 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
2354 while (it != d->hiddenSectionSize.end()) {
2355 if (it.key() > end)
2356 it = d->hiddenSectionSize.erase(it);
2357 else
2358 ++it;
2359 }
2360 }
2361 }
2362 }
2363
2364 int newSectionCount = end + 1;
2365
2366 if (!d->logicalIndices.isEmpty()) {
2367 if (oldCount <= newSectionCount) {
2368 d->logicalIndices.resize(newSectionCount);
2369 d->visualIndices.resize(newSectionCount);
2370 for (int i = oldCount; i < newSectionCount; ++i) {
2371 d->logicalIndices[i] = i;
2372 d->visualIndices[i] = i;
2373 }
2374 } else {
2375 int j = 0;
2376 for (int i = 0; i < oldCount; ++i) {
2377 int v = d->logicalIndices.at(i);
2378 if (v < newSectionCount) {
2379 d->logicalIndices[j] = v;
2380 d->visualIndices[v] = j;
2381 j++;
2382 }
2383 }
2384 d->logicalIndices.resize(newSectionCount);
2385 d->visualIndices.resize(newSectionCount);
2386 }
2387 }
2388
2389 if (d->globalResizeMode == Stretch)
2390 d->stretchSections = newSectionCount;
2391 else if (d->globalResizeMode == ResizeToContents)
2392 d->contentsSections = newSectionCount;
2393
2394 if (newSectionCount > oldCount)
2395 d->createSectionItems(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
2396 //Q_ASSERT(d->headerLength() == d->length);
2397
2398 if (d->sectionCount() != oldCount)
2399 emit sectionCountChanged(oldCount, d->sectionCount());
2400 d->viewport->update();
2401}
2402
2403/*!
2404 \reimp
2405*/
2406
2407void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
2408{
2409 Q_D(QHeaderView);
2410
2411 if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
2412 if (old.isValid() && old.parent() == d->root)
2413 d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
2414 sectionSize(old.column()), d->viewport->height()));
2415 if (current.isValid() && current.parent() == d->root)
2416 d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
2417 sectionSize(current.column()), d->viewport->height()));
2418 } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
2419 if (old.isValid() && old.parent() == d->root)
2420 d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
2421 d->viewport->width(), sectionSize(old.row())));
2422 if (current.isValid() && current.parent() == d->root)
2423 d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2424 d->viewport->width(), sectionSize(current.row())));
2425 }
2426}
2427
2428
2429/*!
2430 \reimp
2431*/
2432
2433bool QHeaderView::event(QEvent *e)
2434{
2435 Q_D(QHeaderView);
2436 switch (e->type()) {
2437 case QEvent::HoverEnter: {
2438 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2439 d->hover = logicalIndexAt(he->pos());
2440 if (d->hover != -1)
2441 updateSection(d->hover);
2442 break; }
2443 case QEvent::Leave:
2444 case QEvent::HoverLeave: {
2445 if (d->hover != -1)
2446 updateSection(d->hover);
2447 d->hover = -1;
2448 break; }
2449 case QEvent::HoverMove: {
2450 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2451 int oldHover = d->hover;
2452 d->hover = logicalIndexAt(he->pos());
2453 if (d->hover != oldHover) {
2454 if (oldHover != -1)
2455 updateSection(oldHover);
2456 if (d->hover != -1)
2457 updateSection(d->hover);
2458 }
2459 break; }
2460 case QEvent::Timer: {
2461 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2462 if (te->timerId() == d->delayedResize.timerId()) {
2463 d->delayedResize.stop();
2464 resizeSections();
2465 }
2466 break; }
2467 case QEvent::StyleChange:
2468 if (!d->customDefaultSectionSize)
2469 d->updateDefaultSectionSizeFromStyle();
2470 break;
2471 default:
2472 break;
2473 }
2474 return QAbstractItemView::event(e);
2475}
2476
2477/*!
2478 \reimp
2479*/
2480
2481void QHeaderView::paintEvent(QPaintEvent *e)
2482{
2483 Q_D(QHeaderView);
2484
2485 if (count() == 0)
2486 return;
2487
2488 QPainter painter(d->viewport);
2489 const QPoint offset = d->scrollDelayOffset;
2490 QRect translatedEventRect = e->rect();
2491 translatedEventRect.translate(offset);
2492
2493 int start = -1;
2494 int end = -1;
2495 if (d->orientation == Qt::Horizontal) {
2496 start = visualIndexAt(translatedEventRect.left());
2497 end = visualIndexAt(translatedEventRect.right());
2498 } else {
2499 start = visualIndexAt(translatedEventRect.top());
2500 end = visualIndexAt(translatedEventRect.bottom());
2501 }
2502
2503 if (d->reverse()) {
2504 start = (start == -1 ? count() - 1 : start);
2505 end = (end == -1 ? 0 : end);
2506 } else {
2507 start = (start == -1 ? 0 : start);
2508 end = (end == -1 ? count() - 1 : end);
2509 }
2510
2511 int tmp = start;
2512 start = qMin(start, end);
2513 end = qMax(tmp, end);
2514
2515 d->prepareSectionSelected(); // clear and resize the bit array
2516
2517 QRect currentSectionRect;
2518 const int width = d->viewport->width();
2519 const int height = d->viewport->height();
2520 const int rtlHorizontalOffset = d->reverse() ? 1 : 0;
2521 for (int i = start; i <= end; ++i) {
2522 if (d->isVisualIndexHidden(i))
2523 continue;
2524 painter.save();
2525 const int logical = logicalIndex(i);
2526 if (d->orientation == Qt::Horizontal) {
2527 currentSectionRect.setRect(sectionViewportPosition(logical) + rtlHorizontalOffset,
2528 0, sectionSize(logical), height);
2529 } else {
2530 currentSectionRect.setRect(0, sectionViewportPosition(logical),
2531 width, sectionSize(logical));
2532 }
2533 currentSectionRect.translate(offset);
2534
2535 QVariant variant = d->model->headerData(logical, d->orientation,
2536 Qt::FontRole);
2537 if (variant.isValid() && variant.canConvert<QFont>()) {
2538 QFont sectionFont = qvariant_cast<QFont>(variant);
2539 painter.setFont(sectionFont);
2540 }
2541 paintSection(&painter, currentSectionRect, logical);
2542 painter.restore();
2543 }
2544
2545 QStyleOption opt;
2546 opt.init(this);
2547 // Paint the area beyond where there are indexes
2548 if (d->reverse()) {
2549 opt.state |= QStyle::State_Horizontal;
2550 if (currentSectionRect.left() > translatedEventRect.left()) {
2551 opt.rect = QRect(translatedEventRect.left(), 0,
2552 currentSectionRect.left() - translatedEventRect.left(), height);
2553 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2554 }
2555 } else if (currentSectionRect.right() < translatedEventRect.right()) {
2556 // paint to the right
2557 opt.state |= QStyle::State_Horizontal;
2558 opt.rect = QRect(currentSectionRect.right() + 1, 0,
2559 translatedEventRect.right() - currentSectionRect.right(), height);
2560 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2561 } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2562 // paint the bottom section
2563 opt.state &= ~QStyle::State_Horizontal;
2564 opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2565 width, height - currentSectionRect.bottom() - 1);
2566 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2567 }
2568
2569#if 0
2570 // ### visualize sections
2571 for (int a = 0, i = 0; i < d->sectionItems.count(); ++i) {
2572 QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2573 if (d->orientation == Qt::Horizontal)
2574 painter.fillRect(a - d->offset, 0, d->sectionItems.at(i).size, 4, color);
2575 else
2576 painter.fillRect(0, a - d->offset, 4, d->sectionItems.at(i).size, color);
2577 a += d->sectionItems.at(i).size;
2578 }
2579
2580#endif
2581}
2582
2583/*!
2584 \reimp
2585*/
2586
2587void QHeaderView::mousePressEvent(QMouseEvent *e)
2588{
2589 Q_D(QHeaderView);
2590 if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2591 return;
2592 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2593 int handle = d->sectionHandleAt(pos);
2594 d->originalSize = -1; // clear the stored original size
2595 if (handle == -1) {
2596 d->pressed = logicalIndexAt(pos);
2597 if (d->clickableSections)
2598 emit sectionPressed(d->pressed);
2599
2600 bool acceptMoveSection = d->movableSections;
2601 if (acceptMoveSection && d->pressed == 0 && !d->allowUserMoveOfSection0)
2602 acceptMoveSection = false; // Do not allow moving the tree nod
2603
2604 if (acceptMoveSection) {
2605 d->section = d->target = d->pressed;
2606 if (d->section == -1)
2607 return;
2608 d->state = QHeaderViewPrivate::MoveSection;
2609 d->setupSectionIndicator(d->section, pos);
2610 } else if (d->clickableSections && d->pressed != -1) {
2611 updateSection(d->pressed);
2612 d->state = QHeaderViewPrivate::SelectSections;
2613 }
2614 } else if (sectionResizeMode(handle) == Interactive) {
2615 d->originalSize = sectionSize(handle);
2616 d->state = QHeaderViewPrivate::ResizeSection;
2617 d->section = handle;
2618 d->preventCursorChangeInSetOffset = false;
2619 }
2620
2621 d->firstPos = pos;
2622 d->lastPos = pos;
2623
2624 d->clearCascadingSections();
2625}
2626
2627/*!
2628 \reimp
2629*/
2630
2631void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2632{
2633 Q_D(QHeaderView);
2634 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2635 if (pos < 0 && d->state != QHeaderViewPrivate::SelectSections)
2636 return;
2637 if (e->buttons() == Qt::NoButton) {
2638#if 1 // Used to be excluded in Qt4 for Q_WS_MAC
2639 // Under Cocoa, when the mouse button is released, may include an extra
2640 // simulated mouse moved event. The state of the buttons when this event
2641 // is generated is already "no button" and the code below gets executed
2642 // just before the mouseReleaseEvent and resets the state. This prevents
2643 // column dragging from working. So this code is disabled under Cocoa.
2644 d->state = QHeaderViewPrivate::NoState;
2645 d->pressed = -1;
2646#endif
2647 }
2648 switch (d->state) {
2649 case QHeaderViewPrivate::ResizeSection: {
2650 Q_ASSERT(d->originalSize != -1);
2651 if (d->cascadingResizing) {
2652 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2653 int visual = visualIndex(d->section);
2654 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2655 } else {
2656 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2657 int newsize = qBound(minimumSectionSize(), d->originalSize + delta, maximumSectionSize());
2658 resizeSection(d->section, newsize);
2659 }
2660 d->lastPos = pos;
2661 return;
2662 }
2663 case QHeaderViewPrivate::MoveSection: {
2664 if (d->shouldAutoScroll(e->pos()))
2665 d->startAutoScroll();
2666 if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2667#if QT_CONFIG(label)
2668 || !d->sectionIndicator->isHidden()
2669#endif
2670 ) {
2671 int visual = visualIndexAt(pos);
2672 if (visual == -1)
2673 return;
2674 if (visual == 0 && logicalIndex(0) == 0 && !d->allowUserMoveOfSection0)
2675 return;
2676
2677 int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2;
2678 int moving = visualIndex(d->section);
2679 if (visual < moving) {
2680 if (pos < posThreshold)
2681 d->target = d->logicalIndex(visual);
2682 else
2683 d->target = d->logicalIndex(visual + 1);
2684 } else if (visual > moving) {
2685 if (pos > posThreshold)
2686 d->target = d->logicalIndex(visual);
2687 else
2688 d->target = d->logicalIndex(visual - 1);
2689 } else {
2690 d->target = d->section;
2691 }
2692 d->updateSectionIndicator(d->section, pos);
2693 }
2694 return;
2695 }
2696 case QHeaderViewPrivate::SelectSections: {
2697 int logical = logicalIndexAt(qMax(-d->offset, pos));
2698 if (logical == -1 && pos > 0)
2699 logical = logicalIndex(d->lastVisibleVisualIndex());
2700 if (logical == d->pressed)
2701 return; // nothing to do
2702 else if (d->pressed != -1)
2703 updateSection(d->pressed);
2704 d->pressed = logical;
2705 if (d->clickableSections && logical != -1) {
2706 emit sectionEntered(d->pressed);
2707 updateSection(d->pressed);
2708 }
2709 return;
2710 }
2711 case QHeaderViewPrivate::NoState: {
2712#ifndef QT_NO_CURSOR
2713 int handle = d->sectionHandleAt(pos);
2714 bool hasCursor = testAttribute(Qt::WA_SetCursor);
2715 if (handle != -1 && (sectionResizeMode(handle) == Interactive)) {
2716 if (!hasCursor)
2717 setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2718 } else {
2719 if (hasCursor)
2720 unsetCursor();
2721#ifndef QT_NO_STATUSTIP
2722 int logical = logicalIndexAt(pos);
2723 QString statusTip;
2724 if (logical != -1)
2725 statusTip = d->model->headerData(logical, d->orientation, Qt::StatusTipRole).toString();
2726 if (d->shouldClearStatusTip || !statusTip.isEmpty()) {
2727 QStatusTipEvent tip(statusTip);
2728 QCoreApplication::sendEvent(d->parent ? d->parent : this, &tip);
2729 d->shouldClearStatusTip = !statusTip.isEmpty();
2730 }
2731#endif // !QT_NO_STATUSTIP
2732 }
2733#endif
2734 return;
2735 }
2736 default:
2737 break;
2738 }
2739}
2740
2741/*!
2742 \reimp
2743*/
2744
2745void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2746{
2747 Q_D(QHeaderView);
2748 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2749 switch (d->state) {
2750 case QHeaderViewPrivate::MoveSection:
2751 if (true
2752#if QT_CONFIG(label)
2753 && !d->sectionIndicator->isHidden()
2754#endif
2755 ) { // moving
2756 int from = visualIndex(d->section);
2757 Q_ASSERT(from != -1);
2758 int to = visualIndex(d->target);
2759 Q_ASSERT(to != -1);
2760 moveSection(from, to);
2761 d->section = d->target = -1;
2762 d->updateSectionIndicator(d->section, pos);
2763 break;
2764 } // not moving
2765 Q_FALLTHROUGH();
2766 case QHeaderViewPrivate::SelectSections:
2767 if (!d->clickableSections) {
2768 int section = logicalIndexAt(pos);
2769 updateSection(section);
2770 }
2771 Q_FALLTHROUGH();
2772 case QHeaderViewPrivate::NoState:
2773 if (d->clickableSections) {
2774 int section = logicalIndexAt(pos);
2775 if (section != -1 && section == d->pressed) {
2776 d->flipSortIndicator(section);
2777 emit sectionClicked(section);
2778 }
2779 if (d->pressed != -1)
2780 updateSection(d->pressed);
2781 }
2782 break;
2783 case QHeaderViewPrivate::ResizeSection:
2784 d->originalSize = -1;
2785 d->clearCascadingSections();
2786 break;
2787 default:
2788 break;
2789 }
2790 d->state = QHeaderViewPrivate::NoState;
2791 d->pressed = -1;
2792}
2793
2794/*!
2795 \reimp
2796*/
2797
2798void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2799{
2800 Q_D(QHeaderView);
2801 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2802 int handle = d->sectionHandleAt(pos);
2803 if (handle > -1 && sectionResizeMode(handle) == Interactive) {
2804 emit sectionHandleDoubleClicked(handle);
2805#ifndef QT_NO_CURSOR
2806 Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2807 ? Qt::SplitHCursor : Qt::SplitVCursor;
2808 if (cursor().shape() == splitCursor) {
2809 // signal handlers may have changed the section size
2810 handle = d->sectionHandleAt(pos);
2811 if (!(handle > -1 && sectionResizeMode(handle) == Interactive))
2812 setCursor(Qt::ArrowCursor);
2813 }
2814#endif
2815 } else {
2816 emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2817 }
2818}
2819
2820/*!
2821 \reimp
2822*/
2823
2824bool QHeaderView::viewportEvent(QEvent *e)
2825{
2826 Q_D(QHeaderView);
2827 switch (e->type()) {
2828#ifndef QT_NO_TOOLTIP
2829 case QEvent::ToolTip: {
2830 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2831 int logical = logicalIndexAt(he->pos());
2832 if (logical != -1) {
2833 QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2834 if (variant.isValid()) {
2835 QToolTip::showText(he->globalPos(), variant.toString(), this);
2836 return true;
2837 }
2838 }
2839 break; }
2840#endif
2841#if QT_CONFIG(whatsthis)
2842 case QEvent::QueryWhatsThis: {
2843 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2844 int logical = logicalIndexAt(he->pos());
2845 if (logical != -1
2846 && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2847 return true;
2848 break; }
2849 case QEvent::WhatsThis: {
2850 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2851 int logical = logicalIndexAt(he->pos());
2852 if (logical != -1) {
2853 QVariant whatsthis = d->model->headerData(logical, d->orientation,
2854 Qt::WhatsThisRole);
2855 if (whatsthis.isValid()) {
2856 QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2857 return true;
2858 }
2859 }
2860 break; }
2861#endif // QT_CONFIG(whatsthis)
2862#if QT_CONFIG(statustip)
2863 case QEvent::StatusTip: {
2864 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2865 int logical = logicalIndexAt(he->pos());
2866 if (logical != -1) {
2867 QString statustip = d->model->headerData(logical, d->orientation,
2868 Qt::StatusTipRole).toString();
2869 if (!statustip.isEmpty())
2870 setStatusTip(statustip);
2871 }
2872 return true; }
2873#endif // QT_CONFIG(statustip)
2874 case QEvent::Resize:
2875 case QEvent::FontChange:
2876 case QEvent::StyleChange:
2877 d->invalidateCachedSizeHint();
2878 Q_FALLTHROUGH();
2879 case QEvent::Hide:
2880 case QEvent::Show: {
2881 QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget());
2882 if (parent && parent->isVisible()) // Only resize if we have a visible parent
2883 resizeSections();
2884 emit geometriesChanged();
2885 break;}
2886 case QEvent::ContextMenu: {
2887 d->state = QHeaderViewPrivate::NoState;
2888 d->pressed = d->section = d->target = -1;
2889 d->updateSectionIndicator(d->section, -1);
2890 break; }
2891 case QEvent::Wheel: {
2892 QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2893 if (asa)
2894 return QApplication::sendEvent(asa->viewport(), e);
2895 break; }
2896 default:
2897 break;
2898 }
2899 return QAbstractItemView::viewportEvent(e);
2900}
2901
2902/*!
2903 Paints the section specified by the given \a logicalIndex, using the given
2904 \a painter and \a rect.
2905
2906 Normally, you do not have to call this function.
2907*/
2908
2909void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2910{
2911 Q_D(const QHeaderView);
2912 if (!rect.isValid())
2913 return;
2914 // get the state of the section
2915 QStyleOptionHeader opt;
2916 initStyleOption(&opt);
2917 QStyle::State state = QStyle::State_None;
2918 if (isEnabled())
2919 state |= QStyle::State_Enabled;
2920 if (window()->isActiveWindow())
2921 state |= QStyle::State_Active;
2922 if (d->clickableSections) {
2923 if (logicalIndex == d->hover)
2924 state |= QStyle::State_MouseOver;
2925 if (logicalIndex == d->pressed)
2926 state |= QStyle::State_Sunken;
2927 else if (d->highlightSelected) {
2928 if (d->sectionIntersectsSelection(logicalIndex))
2929 state |= QStyle::State_On;
2930 if (d->isSectionSelected(logicalIndex))
2931 state |= QStyle::State_Sunken;
2932 }
2933
2934 }
2935 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2936 opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2937 ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2938
2939 // setup the style options structure
2940 QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2941 Qt::TextAlignmentRole);
2942 opt.rect = rect;
2943 opt.section = logicalIndex;
2944 opt.state |= state;
2945 opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2946 ? Qt::Alignment(textAlignment.toInt())
2947 : d->defaultAlignment);
2948
2949 opt.iconAlignment = Qt::AlignVCenter;
2950 opt.text = d->model->headerData(logicalIndex, d->orientation,
2951 Qt::DisplayRole).toString();
2952
2953 int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
2954
2955 const Qt::Alignment headerArrowAlignment = static_cast<Qt::Alignment>(style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this));
2956 const bool isHeaderArrowOnTheSide = headerArrowAlignment & Qt::AlignVCenter;
2957 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex && isHeaderArrowOnTheSide)
2958 margin += style()->pixelMetric(QStyle::PM_HeaderMarkSize, 0, this);
2959
2960 const QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2961 Qt::DecorationRole);
2962 opt.icon = qvariant_cast<QIcon>(variant);
2963 if (opt.icon.isNull())
2964 opt.icon = qvariant_cast<QPixmap>(variant);
2965 if (!opt.icon.isNull()) // see CT_HeaderSection
2966 margin += style()->pixelMetric(QStyle::PM_SmallIconSize, 0, this) +
2967 style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
2968
2969 if (d->textElideMode != Qt::ElideNone) {
2970 const QRect textRect = style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this);
2971 opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode, textRect.width() - margin);
2972 }
2973
2974 QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2975 Qt::ForegroundRole);
2976 if (foregroundBrush.canConvert<QBrush>())
2977 opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2978
2979 QPointF oldBO = painter->brushOrigin();
2980 QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2981 Qt::BackgroundRole);
2982 if (backgroundBrush.canConvert<QBrush>()) {
2983 opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2984 opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2985 painter->setBrushOrigin(opt.rect.topLeft());
2986 }
2987
2988 // the section position
2989 int visual = visualIndex(logicalIndex);
2990 Q_ASSERT(visual != -1);
2991 bool first = d->isFirstVisibleSection(visual);
2992 bool last = d->isLastVisibleSection(visual);
2993 if (first && last)
2994 opt.position = QStyleOptionHeader::OnlyOneSection;
2995 else if (first)
2996 opt.position = d->reverse() ? QStyleOptionHeader::End : QStyleOptionHeader::Beginning;
2997 else if (last)
2998 opt.position = d->reverse() ? QStyleOptionHeader::Beginning : QStyleOptionHeader::End;
2999 else
3000 opt.position = QStyleOptionHeader::Middle;
3001 opt.orientation = d->orientation;
3002 // the selected position
3003 bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
3004 bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
3005 if (previousSelected && nextSelected)
3006 opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
3007 else if (previousSelected)
3008 opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
3009 else if (nextSelected)
3010 opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
3011 else
3012 opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
3013 // draw the section
3014 style()->drawControl(QStyle::CE_Header, &opt, painter, this);
3015
3016 painter->setBrushOrigin(oldBO);
3017}
3018
3019/*!
3020 Returns the size of the contents of the section specified by the given
3021 \a logicalIndex.
3022
3023 \sa defaultSectionSize()
3024*/
3025
3026QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
3027{
3028 Q_D(const QHeaderView);
3029 Q_ASSERT(logicalIndex >= 0);
3030
3031 ensurePolished();
3032
3033 // use SizeHintRole
3034 QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
3035 if (variant.isValid())
3036 return qvariant_cast<QSize>(variant);
3037
3038 // otherwise use the contents
3039 QStyleOptionHeader opt;
3040 initStyleOption(&opt);
3041 opt.section = logicalIndex;
3042 QVariant var = d->model->headerData(logicalIndex, d->orientation,
3043 Qt::FontRole);
3044 QFont fnt;
3045 if (var.isValid() && var.canConvert<QFont>())
3046 fnt = qvariant_cast<QFont>(var);
3047 else
3048 fnt = font();
3049 fnt.setBold(true);
3050 opt.fontMetrics = QFontMetrics(fnt);
3051 opt.text = d->model->headerData(logicalIndex, d->orientation,
3052 Qt::DisplayRole).toString();
3053 variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
3054 opt.icon = qvariant_cast<QIcon>(variant);
3055 if (opt.icon.isNull())
3056 opt.icon = qvariant_cast<QPixmap>(variant);
3057 if (isSortIndicatorShown())
3058 opt.sortIndicator = QStyleOptionHeader::SortDown;
3059 return style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
3060}
3061
3062/*!
3063 Returns the horizontal offset of the header. This is 0 for vertical
3064 headers.
3065
3066 \sa offset()
3067*/
3068
3069int QHeaderView::horizontalOffset() const
3070{
3071 Q_D(const QHeaderView);
3072 if (d->orientation == Qt::Horizontal)
3073 return d->offset;
3074 return 0;
3075}
3076
3077/*!
3078 Returns the vertical offset of the header. This is 0 for horizontal
3079 headers.
3080
3081 \sa offset()
3082*/
3083
3084int QHeaderView::verticalOffset() const
3085{
3086 Q_D(const QHeaderView);
3087 if (d->orientation == Qt::Vertical)
3088 return d->offset;
3089 return 0;
3090}
3091
3092/*!
3093 \reimp
3094 \internal
3095*/
3096
3097void QHeaderView::updateGeometries()
3098{
3099 Q_D(QHeaderView);
3100 d->layoutChildren();
3101 if (d->hasAutoResizeSections())
3102 d->doDelayedResizeSections();
3103}
3104
3105/*!
3106 \reimp
3107 \internal
3108*/
3109
3110void QHeaderView::scrollContentsBy(int dx, int dy)
3111{
3112 Q_D(QHeaderView);
3113 d->scrollDirtyRegion(dx, dy);
3114}
3115
3116/*!
3117 \reimp
3118 \internal
3119*/
3120void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
3121{
3122 Q_D(QHeaderView);
3123 if (!roles.isEmpty()) {
3124 const auto doesRoleAffectSize = [](int role) -> bool {
3125 switch (role) {
3126 case Qt::DisplayRole:
3127 case Qt::DecorationRole:
3128 case Qt::SizeHintRole:
3129 case Qt::FontRole:
3130 return true;
3131 default:
3132 // who knows what a subclass or custom style might do
3133 return role >= Qt::UserRole;
3134 }
3135 };
3136 if (std::none_of(roles.begin(), roles.end(), doesRoleAffectSize))
3137 return;
3138 }
3139 d->invalidateCachedSizeHint();
3140 if (d->hasAutoResizeSections()) {
3141 bool resizeRequired = d->globalResizeMode == ResizeToContents;
3142 int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
3143 int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
3144 for (int i = first; i <= last && !resizeRequired; ++i)
3145 resizeRequired = (sectionResizeMode(i) == ResizeToContents);
3146 if (resizeRequired)
3147 d->doDelayedResizeSections();
3148 }
3149}
3150
3151/*!
3152 \reimp
3153 \internal
3154
3155 Empty implementation because the header doesn't show QModelIndex items.
3156*/
3157void QHeaderView::rowsInserted(const QModelIndex &, int, int)
3158{
3159 // do nothing
3160}
3161
3162/*!
3163 \reimp
3164 \internal
3165
3166 Empty implementation because the header doesn't show QModelIndex items.
3167*/
3168
3169QRect QHeaderView::visualRect(const QModelIndex &) const
3170{
3171 return QRect();
3172}
3173
3174/*!
3175 \reimp
3176 \internal
3177
3178 Empty implementation because the header doesn't show QModelIndex items.
3179*/
3180
3181void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
3182{
3183 // do nothing - the header only displays sections
3184}
3185
3186/*!
3187 \reimp
3188 \internal
3189
3190 Empty implementation because the header doesn't show QModelIndex items.
3191*/
3192
3193QModelIndex QHeaderView::indexAt(const QPoint &) const
3194{
3195 return QModelIndex();
3196}
3197
3198/*!
3199 \reimp
3200 \internal
3201
3202 Empty implementation because the header doesn't show QModelIndex items.
3203*/
3204
3205bool QHeaderView::isIndexHidden(const QModelIndex &) const
3206{
3207 return true; // the header view has no items, just sections
3208}
3209
3210/*!
3211 \reimp
3212 \internal
3213
3214 Empty implementation because the header doesn't show QModelIndex items.
3215*/
3216
3217QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
3218{
3219 return QModelIndex();
3220}
3221
3222/*!
3223 \reimp
3224
3225 Selects the items in the given \a rect according to the specified
3226 \a flags.
3227
3228 The base class implementation does nothing.
3229*/
3230
3231void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
3232{
3233 // do nothing
3234}
3235
3236/*!
3237 \internal
3238*/
3239
3240QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
3241{
3242 Q_D(const QHeaderView);
3243 const int max = d->modelSectionCount();
3244
3245 if (d->orientation == Qt::Horizontal) {
3246 int logicalLeft = max;
3247 int logicalRight = 0;
3248
3249 if (d->visualIndices.empty()) {
3250 // If no reordered sections, skip redundant visual-to-logical transformations
3251 for (const auto &r : selection) {
3252 if (r.parent().isValid() || !r.