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