1 /****************************************************************************
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qheaderview.h"
44 #ifndef QT_NO_ITEMVIEWS
45 #include <qbitarray.h>
50 #include <qscrollbar.h>
52 #include <qwhatsthis.h>
54 #include <qstyleoption.h>
56 #include <qapplication.h>
57 #include <qvarlengtharray.h>
58 #include <qabstractitemdelegate.h>
60 #include <private/qheaderview_p.h>
61 #include <private/qabstractitemmodel_p.h>
63 #ifndef QT_NO_DATASTREAM
64 #include <qdatastream.h>
69 #ifndef QT_NO_DATASTREAM
70 QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionSpan &span)
76 QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionSpan &span)
81 #endif // QT_NO_DATASTREAM
87 \brief The QHeaderView class provides a header row or header column for
93 A QHeaderView displays the headers used in item views such as the
94 QTableView and QTreeView classes. It takes the place of Qt3's \c QHeader
95 class previously used for the same purpose, but uses the Qt's model/view
96 architecture for consistency with the item view classes.
98 The QHeaderView class is one of the \l{Model/View Classes} and is part of
99 Qt's \l{Model/View Programming}{model/view framework}.
101 The header gets the data for each section from the model using the
102 QAbstractItemModel::headerData() function. You can set the data by using
103 QAbstractItemModel::setHeaderData().
105 Each header has an orientation() and a number of sections, given by the
106 count() function. A section refers to a part of the header - either a row
107 or a column, depending on the orientation.
109 Sections can be moved and resized using moveSection() and resizeSection();
110 they can also be hidden and shown with hideSection() and showSection().
112 Each section of a header is described by a section ID, specified by its
113 section(), and can be located at a particular visualIndex() in the header.
114 A section can have a sort indicator set with setSortIndicator(); this
115 indicates whether the items in the associated item view will be sorted in
116 the order given by the section.
118 For a horizontal header the section is equivalent to a column in the model,
119 and for a vertical header the section is equivalent to a row in the model.
121 \section1 Moving Header Sections
123 A header can be fixed in place, or made movable with setMovable(). It can
124 be made clickable with setClickable(), and has resizing behavior in
125 accordance with setResizeMode().
127 \note Double-clicking on a header to resize a section only applies for
130 A header will emit sectionMoved() if the user moves a section,
131 sectionResized() if the user resizes a section, and sectionClicked() as
132 well as sectionHandleDoubleClicked() in response to mouse clicks. A header
133 will also emit sectionCountChanged() and sectionAutoResize().
135 You can identify a section using the logicalIndex() and logicalIndexAt()
136 functions, or by its index position, using the visualIndex() and
137 visualIndexAt() functions. The visual index will change if a section is
138 moved, but the logical index will not change.
142 QTableWidget and QTableView create default headers. If you want
143 the headers to be visible, you can use \l{QFrame::}{setVisible()}.
145 Not all \l{Qt::}{ItemDataRole}s will have an effect on a
146 QHeaderView. If you need to draw other roles, you can subclass
147 QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
148 QHeaderView respects the following item data roles:
149 \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
150 \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
151 \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
153 \note Each header renders the data for each section itself, and does not
154 rely on a delegate. As a result, calling a header's setItemDelegate()
155 function will have no effect.
157 \sa {Model/View Programming}, QListView, QTableView, QTreeView
161 \enum QHeaderView::ResizeMode
163 The resize mode specifies the behavior of the header sections. It can be
164 set on the entire header view or on individual sections using
167 \value Interactive The user can resize the section. The section can also be
168 resized programmatically using resizeSection(). The section size
169 defaults to \l defaultSectionSize. (See also
170 \l cascadingSectionResizes.)
172 \value Fixed The user cannot resize the section. The section can only be
173 resized programmatically using resizeSection(). The section size
174 defaults to \l defaultSectionSize.
176 \value Stretch QHeaderView will automatically resize the section to fill
177 the available space. The size cannot be changed by the user or
180 \value ResizeToContents QHeaderView will automatically resize the section
181 to its optimal size based on the contents of the entire column or
182 row. The size cannot be changed by the user or programmatically.
183 (This value was introduced in 4.2)
185 The following values are obsolete:
186 \value Custom Use Fixed instead.
188 \sa setResizeMode() stretchLastSection minimumSectionSize
192 \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
195 This signal is emitted when a section is moved. The section's logical index
196 is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
197 the new index position by \a newVisualIndex.
203 \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
206 This signal is emitted when a section is resized. The section's logical
207 number is specified by \a logicalIndex, the old size by \a oldSize, and the
208 new size by \a newSize.
214 \fn void QHeaderView::sectionPressed(int logicalIndex)
216 This signal is emitted when a section is pressed. The section's logical
217 index is specified by \a logicalIndex.
223 \fn void QHeaderView::sectionClicked(int logicalIndex)
225 This signal is emitted when a section is clicked. The section's logical
226 index is specified by \a logicalIndex.
228 Note that the sectionPressed signal will also be emitted.
230 \sa setClickable(), sectionPressed()
234 \fn void QHeaderView::sectionEntered(int logicalIndex)
237 This signal is emitted when the cursor moves over the section and the left
238 mouse button is pressed. The section's logical index is specified by
241 \sa setClickable(), sectionPressed()
245 \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
247 This signal is emitted when a section is double-clicked. The section's
248 logical index is specified by \a logicalIndex.
254 \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
256 This signal is emitted when the number of sections changes, i.e., when
257 sections are added or deleted. The original count is specified by
258 \a oldCount, and the new count by \a newCount.
260 \sa count(), length(), headerDataChanged()
264 \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
266 This signal is emitted when a section is double-clicked. The section's
267 logical index is specified by \a logicalIndex.
273 \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
277 This signal is emitted when the section containing the sort indicator or
278 the order indicated is changed. The section's logical index is specified
279 by \a logicalIndex and the sort order is specified by \a order.
281 \sa setSortIndicator()
285 \fn void QHeaderView::sectionAutoResize(int logicalIndex,
286 QHeaderView::ResizeMode mode)
288 This signal is emitted when a section is automatically resized. The
289 section's logical index is specified by \a logicalIndex, and the resize
292 \sa setResizeMode(), stretchLastSection()
294 // ### Qt 5: change to sectionAutoResized()
297 \fn void QHeaderView::geometriesChanged()
300 This signal is emitted when the header's geometries have changed.
304 \property QHeaderView::highlightSections
305 \brief whether the sections containing selected items are highlighted
307 By default, this property is false.
311 Creates a new generic header with the given \a orientation and \a parent.
313 QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
314 : QAbstractItemView(*new QHeaderViewPrivate, parent)
317 d->setDefaultValues(orientation);
324 QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
325 Qt::Orientation orientation, QWidget *parent)
326 : QAbstractItemView(dd, parent)
329 d->setDefaultValues(orientation);
337 QHeaderView::~QHeaderView()
344 void QHeaderView::initialize()
347 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
348 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
349 setFrameStyle(NoFrame);
350 setFocusPolicy(Qt::NoFocus);
351 d->viewport->setMouseTracking(true);
352 d->viewport->setBackgroundRole(QPalette::Button);
353 d->textElideMode = Qt::ElideNone;
354 delete d->itemDelegate;
360 void QHeaderView::setModel(QAbstractItemModel *model)
362 if (model == this->model())
365 if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
366 if (d->orientation == Qt::Horizontal) {
367 QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
368 this, SLOT(sectionsInserted(QModelIndex,int,int)));
369 QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
370 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
371 QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
372 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
374 QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
375 this, SLOT(sectionsInserted(QModelIndex,int,int)));
376 QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
377 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
378 QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
379 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
381 QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
382 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
383 QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
384 this, SLOT(_q_layoutAboutToBeChanged()));
387 if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
388 if (d->orientation == Qt::Horizontal) {
389 QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
390 this, SLOT(sectionsInserted(QModelIndex,int,int)));
391 QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
392 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
393 QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
394 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
396 QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
397 this, SLOT(sectionsInserted(QModelIndex,int,int)));
398 QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
399 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
400 QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
401 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
403 QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
404 this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
405 QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
406 this, SLOT(_q_layoutAboutToBeChanged()));
409 d->state = QHeaderViewPrivate::NoClear;
410 QAbstractItemView::setModel(model);
411 d->state = QHeaderViewPrivate::NoState;
413 // Users want to set sizes and modes before the widget is shown.
414 // Thus, we have to initialize when the model is set,
415 // and not lazily like we do in the other views.
416 initializeSections();
420 Returns the orientation of the header.
425 Qt::Orientation QHeaderView::orientation() const
427 Q_D(const QHeaderView);
428 return d->orientation;
432 Returns the offset of the header: this is the header's left-most (or
433 top-most for vertical headers) visible pixel.
438 int QHeaderView::offset() const
440 Q_D(const QHeaderView);
445 \fn void QHeaderView::setOffset(int offset)
447 Sets the header's offset to \a offset.
449 \sa offset(), length()
452 void QHeaderView::setOffset(int newOffset)
455 if (d->offset == (int)newOffset)
457 int ndelta = d->offset - newOffset;
458 d->offset = newOffset;
459 if (d->orientation == Qt::Horizontal)
460 d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
462 d->viewport->scroll(0, ndelta);
463 if (d->state == QHeaderViewPrivate::ResizeSection) {
464 QPoint cursorPos = QCursor::pos();
465 if (d->orientation == Qt::Horizontal)
466 QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
468 QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
469 d->firstPos += ndelta;
470 d->lastPos += ndelta;
476 Sets the offset to the start of the section at the given \a visualIndex.
478 \sa setOffset(), sectionPosition()
480 void QHeaderView::setOffsetToSectionPosition(int visualIndex)
483 if (visualIndex > -1 && visualIndex < d->sectionCount) {
484 int position = d->headerSectionPosition(d->adjustedVisualIndex(visualIndex));
491 Sets the offset to make the last section visible.
493 \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
495 void QHeaderView::setOffsetToLastSection()
497 Q_D(const QHeaderView);
498 int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
499 int position = length() - size;
504 Returns the length along the orientation of the header.
506 \sa sizeHint(), setResizeMode(), offset()
509 int QHeaderView::length() const
511 Q_D(const QHeaderView);
512 d->executePostedLayout();
513 d->executePostedResize();
514 //Q_ASSERT(d->headerLength() == d->length);
519 Returns a suitable size hint for this header.
521 \sa sectionSizeHint()
524 QSize QHeaderView::sizeHint() const
526 Q_D(const QHeaderView);
527 if (d->cachedSizeHint.isValid())
528 return d->cachedSizeHint;
529 d->cachedSizeHint = QSize(0, 0); //reinitialize the cached size hint
530 const int sectionCount = count();
532 // get size hint for the first n sections
534 for (int checked = 0; checked < 100 && i < sectionCount; ++i) {
535 if (isSectionHidden(i))
538 QSize hint = sectionSizeFromContents(i);
539 d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
541 // get size hint for the last n sections
542 i = qMax(i, sectionCount - 100 );
543 for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) {
544 if (isSectionHidden(j))
547 QSize hint = sectionSizeFromContents(j);
548 d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
550 return d->cachedSizeHint;
554 Returns a suitable size hint for the section specified by \a logicalIndex.
556 \sa sizeHint(), defaultSectionSize(), minimumSectionSize(),
560 int QHeaderView::sectionSizeHint(int logicalIndex) const
562 Q_D(const QHeaderView);
563 if (isSectionHidden(logicalIndex))
565 if (logicalIndex < 0 || logicalIndex >= count())
568 QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
570 size = qvariant_cast<QSize>(value);
572 size = sectionSizeFromContents(logicalIndex);
573 int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
574 return qMax(minimumSectionSize(), hint);
578 Returns the visual index of the section that covers the given \a position
584 int QHeaderView::visualIndexAt(int position) const
586 Q_D(const QHeaderView);
587 int vposition = position;
588 d->executePostedLayout();
589 d->executePostedResize();
590 const int count = d->sectionCount;
595 vposition = d->viewport->width() - vposition;
596 vposition += d->offset;
598 if (vposition > d->length)
600 int visual = d->headerVisualIndexAt(vposition);
604 while (d->isVisualIndexHidden(visual)){
613 Returns the section that covers the given \a position in the viewport.
615 \sa visualIndexAt(), isSectionHidden()
618 int QHeaderView::logicalIndexAt(int position) const
620 const int visual = visualIndexAt(position);
622 return logicalIndex(visual);
627 Returns the width (or height for vertical headers) of the given
630 \sa length(), setResizeMode(), defaultSectionSize()
633 int QHeaderView::sectionSize(int logicalIndex) const
635 Q_D(const QHeaderView);
636 if (isSectionHidden(logicalIndex))
638 if (logicalIndex < 0 || logicalIndex >= count())
640 int visual = visualIndex(logicalIndex);
643 d->executePostedResize();
644 return d->headerSectionSize(visual);
649 Returns the section position of the given \a logicalIndex, or -1
650 if the section is hidden. The position is measured in pixels from
651 the first visible item's top-left corner to the top-left corner of
652 the item with \a logicalIndex. The measurement is along the x-axis
653 for horizontal headers and along the y-axis for vertical headers.
655 \sa sectionViewportPosition()
658 int QHeaderView::sectionPosition(int logicalIndex) const
660 Q_D(const QHeaderView);
661 int visual = visualIndex(logicalIndex);
662 // in some cases users may change the selections
663 // before we have a chance to do the layout
666 d->executePostedResize();
667 return d->headerSectionPosition(visual);
671 Returns the section viewport position of the given \a logicalIndex.
673 If the section is hidden, the return value is undefined.
675 \sa sectionPosition(), isSectionHidden()
678 int QHeaderView::sectionViewportPosition(int logicalIndex) const
680 Q_D(const QHeaderView);
681 if (logicalIndex >= count())
683 int position = sectionPosition(logicalIndex);
685 return position; // the section was hidden
686 int offsetPosition = position - d->offset;
688 return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
689 return offsetPosition;
693 \fn int QHeaderView::logicalIndexAt(int x, int y) const
695 Returns the logical index of the section at the given coordinate. If the
696 header is horizontal \a x will be used, otherwise \a y will be used to
697 find the logical index.
701 \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
703 Returns the logical index of the section at the position given in \a pos.
704 If the header is horizontal the x-coordinate will be used, otherwise the
705 y-coordinate will be used to find the logical index.
707 \sa sectionPosition()
711 Moves the section at visual index \a from to occupy visual index \a to.
716 void QHeaderView::moveSection(int from, int to)
720 d->executePostedLayout();
721 if (from < 0 || from >= d->sectionCount || to < 0 || to >= d->sectionCount)
725 int logical = logicalIndex(from);
726 Q_ASSERT(logical != -1);
727 updateSection(logical);
731 if (stretchLastSection() && to == d->lastVisibleVisualIndex())
732 d->lastSectionSize = sectionSize(from);
734 //int oldHeaderLength = length(); // ### for debugging; remove later
735 d->initializeIndexMapping();
737 QBitArray sectionHidden = d->sectionHidden;
738 int *visualIndices = d->visualIndices.data();
739 int *logicalIndices = d->logicalIndices.data();
740 int logical = logicalIndices[from];
743 int affected_count = qAbs(to - from) + 1;
744 QVarLengthArray<int> sizes(affected_count);
745 QVarLengthArray<ResizeMode> modes(affected_count);
747 // move sections and indices
749 sizes[to - from] = d->headerSectionSize(from);
750 modes[to - from] = d->headerSectionResizeMode(from);
751 while (visual < to) {
752 sizes[visual - from] = d->headerSectionSize(visual + 1);
753 modes[visual - from] = d->headerSectionResizeMode(visual + 1);
754 if (!sectionHidden.isEmpty())
755 sectionHidden.setBit(visual, sectionHidden.testBit(visual + 1));
756 visualIndices[logicalIndices[visual + 1]] = visual;
757 logicalIndices[visual] = logicalIndices[visual + 1];
761 sizes[0] = d->headerSectionSize(from);
762 modes[0] = d->headerSectionResizeMode(from);
763 while (visual > to) {
764 sizes[visual - to] = d->headerSectionSize(visual - 1);
765 modes[visual - to] = d->headerSectionResizeMode(visual - 1);
766 if (!sectionHidden.isEmpty())
767 sectionHidden.setBit(visual, sectionHidden.testBit(visual - 1));
768 visualIndices[logicalIndices[visual - 1]] = visual;
769 logicalIndices[visual] = logicalIndices[visual - 1];
773 if (!sectionHidden.isEmpty()) {
774 sectionHidden.setBit(to, d->sectionHidden.testBit(from));
775 d->sectionHidden = sectionHidden;
777 visualIndices[logical] = to;
778 logicalIndices[to] = logical;
780 //Q_ASSERT(oldHeaderLength == length());
782 // ### check for spans of section sizes here
784 for (visual = from; visual <= to; ++visual) {
785 int size = sizes[visual - from];
786 ResizeMode mode = modes[visual - from];
787 d->createSectionSpan(visual, visual, size, mode);
790 for (visual = to; visual <= from; ++visual) {
791 int size = sizes[visual - to];
792 ResizeMode mode = modes[visual - to];
793 d->createSectionSpan(visual, visual, size, mode);
796 //Q_ASSERT(d->headerLength() == length());
797 //Q_ASSERT(oldHeaderLength == length());
798 //Q_ASSERT(d->logicalIndices.count() == d->sectionCount);
800 if (d->hasAutoResizeSections())
801 d->doDelayedResizeSections();
802 d->viewport->update();
804 emit sectionMoved(logical, from, to);
809 Swaps the section at visual index \a first with the section at visual
814 void QHeaderView::swapSections(int first, int second)
820 d->executePostedLayout();
821 if (first < 0 || first >= d->sectionCount || second < 0 || second >= d->sectionCount)
824 int firstSize = d->headerSectionSize(first);
825 ResizeMode firstMode = d->headerSectionResizeMode(first);
826 int firstLogical = d->logicalIndex(first);
828 int secondSize = d->headerSectionSize(second);
829 ResizeMode secondMode = d->headerSectionResizeMode(second);
830 int secondLogical = d->logicalIndex(second);
832 d->createSectionSpan(second, second, firstSize, firstMode);
833 d->createSectionSpan(first, first, secondSize, secondMode);
835 d->initializeIndexMapping();
837 d->visualIndices[firstLogical] = second;
838 d->logicalIndices[second] = firstLogical;
840 d->visualIndices[secondLogical] = first;
841 d->logicalIndices[first] = secondLogical;
843 if (!d->sectionHidden.isEmpty()) {
844 bool firstHidden = d->sectionHidden.testBit(first);
845 bool secondHidden = d->sectionHidden.testBit(second);
846 d->sectionHidden.setBit(first, secondHidden);
847 d->sectionHidden.setBit(second, firstHidden);
850 d->viewport->update();
851 emit sectionMoved(firstLogical, first, second);
852 emit sectionMoved(secondLogical, second, first);
856 \fn void QHeaderView::resizeSection(int logicalIndex, int size)
858 Resizes the section specified by \a logicalIndex to \a size measured in
861 \sa sectionResized(), resizeMode(), sectionSize()
864 void QHeaderView::resizeSection(int logical, int size)
867 if (logical < 0 || logical >= count())
870 if (isSectionHidden(logical)) {
871 d->hiddenSectionSize.insert(logical, size);
875 int visual = visualIndex(logical);
879 int oldSize = d->headerSectionSize(visual);
883 d->executePostedLayout();
884 d->invalidateCachedSizeHint();
886 if (stretchLastSection() && visual == d->lastVisibleVisualIndex())
887 d->lastSectionSize = size;
890 d->createSectionSpan(visual, visual, size, d->headerSectionResizeMode(visual));
892 if (!updatesEnabled()) {
893 if (d->hasAutoResizeSections())
894 d->doDelayedResizeSections();
895 emit sectionResized(logical, oldSize, size);
899 int w = d->viewport->width();
900 int h = d->viewport->height();
901 int pos = sectionViewportPosition(logical);
903 if (d->orientation == Qt::Horizontal)
905 r.setRect(0, 0, pos + size, h);
907 r.setRect(pos, 0, w - pos, h);
909 r.setRect(0, pos, w, h - pos);
911 if (d->hasAutoResizeSections()) {
912 d->doDelayedResizeSections();
913 r = d->viewport->rect();
915 d->viewport->update(r.normalized());
916 emit sectionResized(logical, oldSize, size);
920 Resizes the sections according to the given \a mode, ignoring the current
923 \sa resizeMode(), sectionResized()
926 void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
929 d->resizeSections(mode, true);
933 \fn void QHeaderView::hideSection(int logicalIndex)
934 Hides the section specified by \a logicalIndex.
936 \sa showSection(), isSectionHidden(), hiddenSectionCount(),
941 \fn void QHeaderView::showSection(int logicalIndex)
942 Shows the section specified by \a logicalIndex.
944 \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
949 Returns true if the section specified by \a logicalIndex is explicitly
950 hidden from the user; otherwise returns false.
952 \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
955 bool QHeaderView::isSectionHidden(int logicalIndex) const
957 Q_D(const QHeaderView);
958 d->executePostedLayout();
959 if (logicalIndex >= d->sectionHidden.count() || logicalIndex < 0 || logicalIndex >= d->sectionCount)
961 int visual = visualIndex(logicalIndex);
962 Q_ASSERT(visual != -1);
963 return d->sectionHidden.testBit(visual);
969 Returns the number of sections in the header that has been hidden.
971 \sa setSectionHidden(), isSectionHidden()
973 int QHeaderView::hiddenSectionCount() const
975 Q_D(const QHeaderView);
976 return d->hiddenSectionSize.count();
980 If \a hide is true the section specified by \a logicalIndex is hidden;
981 otherwise the section is shown.
983 \sa isSectionHidden(), hiddenSectionCount()
986 void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
989 if (logicalIndex < 0 || logicalIndex >= count())
992 d->executePostedLayout();
993 int visual = visualIndex(logicalIndex);
994 Q_ASSERT(visual != -1);
995 if (hide == d->isVisualIndexHidden(visual))
998 int size = d->headerSectionSize(visual);
999 if (!d->hasAutoResizeSections())
1000 resizeSection(logicalIndex, 0);
1001 d->hiddenSectionSize.insert(logicalIndex, size);
1002 if (d->sectionHidden.count() < count())
1003 d->sectionHidden.resize(count());
1004 d->sectionHidden.setBit(visual, true);
1005 if (d->hasAutoResizeSections())
1006 d->doDelayedResizeSections();
1008 int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
1009 d->hiddenSectionSize.remove(logicalIndex);
1010 if (d->hiddenSectionSize.isEmpty()) {
1011 d->sectionHidden.clear();
1013 Q_ASSERT(visual <= d->sectionHidden.count());
1014 d->sectionHidden.setBit(visual, false);
1016 resizeSection(logicalIndex, size);
1021 Returns the number of sections in the header.
1023 \sa sectionCountChanged(), length()
1026 int QHeaderView::count() const
1028 Q_D(const QHeaderView);
1029 //Q_ASSERT(d->sectionCount == d->headerSectionCount());
1030 // ### this may affect the lazy layout
1031 d->executePostedLayout();
1032 return d->sectionCount;
1036 Returns the visual index position of the section specified by the given
1037 \a logicalIndex, or -1 otherwise.
1039 Hidden sections still have valid visual indexes.
1044 int QHeaderView::visualIndex(int logicalIndex) const
1046 Q_D(const QHeaderView);
1047 if (logicalIndex < 0)
1049 d->executePostedLayout();
1050 if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
1051 if (logicalIndex < d->sectionCount)
1052 return logicalIndex;
1053 } else if (logicalIndex < d->visualIndices.count()) {
1054 int visual = d->visualIndices.at(logicalIndex);
1055 Q_ASSERT(visual < d->sectionCount);
1062 Returns the logicalIndex for the section at the given \a visualIndex
1063 position, or -1 if visualIndex < 0 or visualIndex >= QHeaderView::count().
1065 Note that the visualIndex is not affected by hidden sections.
1067 \sa visualIndex(), sectionPosition()
1070 int QHeaderView::logicalIndex(int visualIndex) const
1072 Q_D(const QHeaderView);
1073 if (visualIndex < 0 || visualIndex >= d->sectionCount)
1075 return d->logicalIndex(visualIndex);
1079 If \a movable is true, the header may be moved by the user; otherwise it
1082 \sa isMovable(), sectionMoved()
1085 // ### Qt 5: change to setSectionsMovable()
1086 void QHeaderView::setMovable(bool movable)
1089 d->movableSections = movable;
1093 Returns true if the header can be moved by the user; otherwise returns
1099 // ### Qt 5: change to sectionsMovable()
1100 bool QHeaderView::isMovable() const
1102 Q_D(const QHeaderView);
1103 return d->movableSections;
1107 If \a clickable is true, the header will respond to single clicks.
1109 \sa isClickable(), sectionClicked(), sectionPressed(),
1110 setSortIndicatorShown()
1113 // ### Qt 5: change to setSectionsClickable()
1114 void QHeaderView::setClickable(bool clickable)
1117 d->clickableSections = clickable;
1121 Returns true if the header is clickable; otherwise returns false. A
1122 clickable header could be set up to allow the user to change the
1123 representation of the data in the view related to the header.
1128 // ### Qt 5: change to sectionsClickable()
1129 bool QHeaderView::isClickable() const
1131 Q_D(const QHeaderView);
1132 return d->clickableSections;
1135 void QHeaderView::setHighlightSections(bool highlight)
1138 d->highlightSelected = highlight;
1141 bool QHeaderView::highlightSections() const
1143 Q_D(const QHeaderView);
1144 return d->highlightSelected;
1148 Sets the constraints on how the header can be resized to those described
1149 by the given \a mode.
1151 \sa resizeMode(), length(), sectionResized(), sectionAutoResize()
1154 void QHeaderView::setResizeMode(ResizeMode mode)
1157 initializeSections();
1158 d->stretchSections = (mode == Stretch ? count() : 0);
1159 d->contentsSections = (mode == ResizeToContents ? count() : 0);
1160 d->setGlobalHeaderResizeMode(mode);
1161 if (d->hasAutoResizeSections())
1162 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1168 Sets the constraints on how the section specified by \a logicalIndex in
1169 the header can be resized to those described by the given \a mode. The logical
1170 index should exist at the time this function is called.
1172 \note This setting will be ignored for the last section if the stretchLastSection
1173 property is set to true. This is the default for the horizontal headers provided
1176 \sa setStretchLastSection()
1179 // ### Qt 5: change to setSectionResizeMode()
1180 void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
1183 int visual = visualIndex(logicalIndex);
1184 Q_ASSERT(visual != -1);
1186 ResizeMode old = d->headerSectionResizeMode(visual);
1187 d->setHeaderSectionResizeMode(visual, mode);
1189 if (mode == Stretch && old != Stretch)
1190 ++d->stretchSections;
1191 else if (mode == ResizeToContents && old != ResizeToContents)
1192 ++d->contentsSections;
1193 else if (mode != Stretch && old == Stretch)
1194 --d->stretchSections;
1195 else if (mode != ResizeToContents && old == ResizeToContents)
1196 --d->contentsSections;
1198 if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
1199 d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1203 Returns the resize mode that applies to the section specified by the given
1209 QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
1211 Q_D(const QHeaderView);
1212 int visual = visualIndex(logicalIndex);
1214 return Fixed; //the default value
1215 return d->headerSectionResizeMode(visual);
1221 Returns the number of sections that are set to resize mode stretch. In
1222 views, this can be used to see if the headerview needs to resize the
1223 sections when the view's geometry changes.
1225 \sa stretchLastSection, resizeMode()
1228 int QHeaderView::stretchSectionCount() const
1230 Q_D(const QHeaderView);
1231 return d->stretchSections;
1235 \property QHeaderView::showSortIndicator
1236 \brief whether the sort indicator is shown
1238 By default, this property is false.
1243 void QHeaderView::setSortIndicatorShown(bool show)
1246 if (d->sortIndicatorShown == show)
1249 d->sortIndicatorShown = show;
1251 if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
1254 if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents)
1257 d->viewport->update();
1260 bool QHeaderView::isSortIndicatorShown() const
1262 Q_D(const QHeaderView);
1263 return d->sortIndicatorShown;
1267 Sets the sort indicator for the section specified by the given
1268 \a logicalIndex in the direction specified by \a order, and removes the
1269 sort indicator from any other section that was showing it.
1271 \a logicalIndex may be -1, in which case no sort indicator will be shown
1272 and the model will return to its natural, unsorted order. Note that not
1273 all models support this and may even crash in this case.
1275 \sa sortIndicatorSection() sortIndicatorOrder()
1278 void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
1282 // This is so that people can set the position of the sort indicator before the fill the model
1283 int old = d->sortIndicatorSection;
1284 d->sortIndicatorSection = logicalIndex;
1285 d->sortIndicatorOrder = order;
1287 if (logicalIndex >= d->sectionCount) {
1288 emit sortIndicatorChanged(logicalIndex, order);
1289 return; // nothing to do
1292 if (old != logicalIndex
1293 && ((logicalIndex >= 0 && resizeMode(logicalIndex) == ResizeToContents)
1294 || old >= d->sectionCount || (old >= 0 && resizeMode(old) == ResizeToContents))) {
1296 d->viewport->update();
1298 if (old >= 0 && old != logicalIndex)
1300 if (logicalIndex >= 0)
1301 updateSection(logicalIndex);
1304 emit sortIndicatorChanged(logicalIndex, order);
1308 Returns the logical index of the section that has a sort indicator.
1309 By default this is section 0.
1311 \sa setSortIndicator() sortIndicatorOrder() setSortIndicatorShown()
1314 int QHeaderView::sortIndicatorSection() const
1316 Q_D(const QHeaderView);
1317 return d->sortIndicatorSection;
1321 Returns the order for the sort indicator. If no section has a sort
1322 indicator the return value of this function is undefined.
1324 \sa setSortIndicator() sortIndicatorSection()
1327 Qt::SortOrder QHeaderView::sortIndicatorOrder() const
1329 Q_D(const QHeaderView);
1330 return d->sortIndicatorOrder;
1334 \property QHeaderView::stretchLastSection
1335 \brief whether the last visible section in the header takes up all the
1338 The default value is false.
1340 \note The horizontal headers provided by QTreeView are configured with this
1341 property set to true, ensuring that the view does not waste any of the
1342 space assigned to it for its header. If this value is set to true, this
1343 property will override the resize mode set on the last section in the
1348 bool QHeaderView::stretchLastSection() const
1350 Q_D(const QHeaderView);
1351 return d->stretchLastSection;
1354 void QHeaderView::setStretchLastSection(bool stretch)
1357 d->stretchLastSection = stretch;
1358 if (d->state != QHeaderViewPrivate::NoState)
1363 resizeSection(count() - 1, d->defaultSectionSize);
1368 \property QHeaderView::cascadingSectionResizes
1369 \brief whether interactive resizing will be cascaded to the following
1370 sections once the section being resized by the user has reached its
1373 This property only affects sections that have \l Interactive as their
1376 The default value is false.
1380 bool QHeaderView::cascadingSectionResizes() const
1382 Q_D(const QHeaderView);
1383 return d->cascadingResizing;
1386 void QHeaderView::setCascadingSectionResizes(bool enable)
1389 d->cascadingResizing = enable;
1393 \property QHeaderView::defaultSectionSize
1394 \brief the default size of the header sections before resizing.
1396 This property only affects sections that have \l Interactive or \l Fixed
1397 as their resize mode.
1399 \sa setResizeMode() minimumSectionSize
1401 int QHeaderView::defaultSectionSize() const
1403 Q_D(const QHeaderView);
1404 return d->defaultSectionSize;
1407 void QHeaderView::setDefaultSectionSize(int size)
1410 d->setDefaultSectionSize(size);
1415 \property QHeaderView::minimumSectionSize
1416 \brief the minimum size of the header sections.
1418 The minimum section size is the smallest section size allowed. If the
1419 minimum section size is set to -1, QHeaderView will use the maximum of
1420 the \l{QApplication::globalStrut()}{global strut} or the
1421 \l{fontMetrics()}{font metrics} size.
1423 This property is honored by all \l{ResizeMode}{resize modes}.
1425 \sa setResizeMode() defaultSectionSize
1427 int QHeaderView::minimumSectionSize() const
1429 Q_D(const QHeaderView);
1430 if (d->minimumSectionSize == -1) {
1431 QSize strut = QApplication::globalStrut();
1432 int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
1433 if (d->orientation == Qt::Horizontal)
1434 return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
1435 return qMax(strut.height(), (fontMetrics().height() + margin));
1437 return d->minimumSectionSize;
1440 void QHeaderView::setMinimumSectionSize(int size)
1443 d->minimumSectionSize = size;
1448 \property QHeaderView::defaultAlignment
1449 \brief the default alignment of the text in each header section
1452 Qt::Alignment QHeaderView::defaultAlignment() const
1454 Q_D(const QHeaderView);
1455 return d->defaultAlignment;
1458 void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
1461 if (d->defaultAlignment == alignment)
1464 d->defaultAlignment = alignment;
1465 d->viewport->update();
1471 void QHeaderView::doItemsLayout()
1473 initializeSections();
1474 QAbstractItemView::doItemsLayout();
1478 Returns true if sections in the header has been moved; otherwise returns
1483 bool QHeaderView::sectionsMoved() const
1485 Q_D(const QHeaderView);
1486 return !d->visualIndices.isEmpty();
1492 Returns true if sections in the header has been hidden; otherwise returns
1495 \sa setSectionHidden()
1497 bool QHeaderView::sectionsHidden() const
1499 Q_D(const QHeaderView);
1500 return !d->hiddenSectionSize.isEmpty();
1503 #ifndef QT_NO_DATASTREAM
1507 Saves the current state of this header view.
1509 To restore the saved state, pass the return value to restoreState().
1513 QByteArray QHeaderView::saveState() const
1515 Q_D(const QHeaderView);
1517 QDataStream stream(&data, QIODevice::WriteOnly);
1518 stream << QHeaderViewPrivate::VersionMarker;
1519 stream << 0; // current version is 0
1526 Restores the \a state of this header view.
1527 This function returns \c true if the state was restored; otherwise returns
1532 bool QHeaderView::restoreState(const QByteArray &state)
1535 if (state.isEmpty())
1537 QByteArray data = state;
1538 QDataStream stream(&data, QIODevice::ReadOnly);
1543 if (stream.status() != QDataStream::Ok
1544 || marker != QHeaderViewPrivate::VersionMarker
1545 || ver != 0) // current version is 0
1548 if (d->read(stream)) {
1549 emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
1550 d->viewport->update();
1555 #endif // QT_NO_DATASTREAM
1560 void QHeaderView::reset()
1562 QAbstractItemView::reset();
1563 // it would be correct to call clear, but some apps rely
1564 // on the header keeping the sections, even after calling reset
1566 initializeSections();
1570 Updates the changed header sections with the given \a orientation, from
1571 \a logicalFirst to \a logicalLast inclusive.
1573 void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
1576 if (d->orientation != orientation)
1579 if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
1582 d->invalidateCachedSizeHint();
1584 int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
1586 for (int section = logicalFirst; section <= logicalLast; ++section) {
1587 const int visual = visualIndex(section);
1588 firstVisualIndex = qMin(firstVisualIndex, visual);
1589 lastVisualIndex = qMax(lastVisualIndex, visual);
1592 d->executePostedResize();
1593 const int first = d->headerSectionPosition(firstVisualIndex),
1594 last = d->headerSectionPosition(lastVisualIndex)
1595 + d->headerSectionSize(lastVisualIndex);
1597 if (orientation == Qt::Horizontal) {
1598 d->viewport->update(first, 0, last - first, d->viewport->height());
1600 d->viewport->update(0, first, d->viewport->width(), last - first);
1608 Updates the section specified by the given \a logicalIndex.
1611 void QHeaderView::updateSection(int logicalIndex)
1614 if (d->orientation == Qt::Horizontal)
1615 d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
1616 0, sectionSize(logicalIndex), d->viewport->height()));
1618 d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
1619 d->viewport->width(), sectionSize(logicalIndex)));
1623 Resizes the sections according to their size hints. Normally, you do not
1624 have to call this function.
1627 void QHeaderView::resizeSections()
1630 if (d->hasAutoResizeSections())
1631 d->resizeSections(Interactive, false); // no global resize mode
1635 This slot is called when sections are inserted into the \a parent.
1636 \a logicalFirst and \a logicalLast indices signify where the new sections
1639 If only one section is inserted, \a logicalFirst and \a logicalLast will
1643 void QHeaderView::sectionsInserted(const QModelIndex &parent,
1644 int logicalFirst, int logicalLast)
1647 if (parent != d->root)
1648 return; // we only handle changes in the top level
1649 int oldCount = d->sectionCount;
1651 d->invalidateCachedSizeHint();
1653 // add the new sections
1655 for (int spanStart = 0; insertAt < d->sectionSpans.count() && spanStart < logicalFirst; ++insertAt)
1656 spanStart += d->sectionSpans.at(insertAt).count;
1658 int insertCount = logicalLast - logicalFirst + 1;
1659 d->sectionCount += insertCount;
1661 if (d->sectionSpans.isEmpty() || insertAt >= d->sectionSpans.count()) {
1662 int insertLength = d->defaultSectionSize * insertCount;
1663 d->length += insertLength;
1664 QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
1665 d->sectionSpans.append(span);
1666 } else if ((d->sectionSpans.at(insertAt).sectionSize() == d->defaultSectionSize)
1667 && d->sectionSpans.at(insertAt).resizeMode == d->globalResizeMode) {
1668 // add the new sections to an existing span
1669 int insertLength = d->sectionSpans.at(insertAt).sectionSize() * insertCount;
1670 d->length += insertLength;
1671 d->sectionSpans[insertAt].size += insertLength;
1672 d->sectionSpans[insertAt].count += insertCount;
1674 // separate them out into their own span
1675 int insertLength = d->defaultSectionSize * insertCount;
1676 d->length += insertLength;
1677 QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
1678 d->sectionSpans.insert(insertAt, span);
1681 // update sorting column
1682 if (d->sortIndicatorSection >= logicalFirst)
1683 d->sortIndicatorSection += insertCount;
1685 // update resize mode section counts
1686 if (d->globalResizeMode == Stretch)
1687 d->stretchSections = d->sectionCount;
1688 else if (d->globalResizeMode == ResizeToContents)
1689 d->contentsSections = d->sectionCount;
1691 // clear selection cache
1692 d->sectionSelected.clear();
1695 if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
1696 Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
1697 int mappingCount = d->visualIndices.count();
1698 for (int i = 0; i < mappingCount; ++i) {
1699 if (d->visualIndices.at(i) >= logicalFirst)
1700 d->visualIndices[i] += insertCount;
1701 if (d->logicalIndices.at(i) >= logicalFirst)
1702 d->logicalIndices[i] += insertCount;
1704 for (int j = logicalFirst; j <= logicalLast; ++j) {
1705 d->visualIndices.insert(j, j);
1706 d->logicalIndices.insert(j, j);
1710 // insert sections into sectionsHidden
1711 if (!d->sectionHidden.isEmpty()) {
1712 QBitArray sectionHidden(d->sectionHidden);
1713 sectionHidden.resize(sectionHidden.count() + insertCount);
1714 sectionHidden.fill(false, logicalFirst, logicalLast + 1);
1715 for (int j = logicalLast + 1; j < sectionHidden.count(); ++j)
1716 //here we simply copy the old sectionHidden
1717 sectionHidden.setBit(j, d->sectionHidden.testBit(j - insertCount));
1718 d->sectionHidden = sectionHidden;
1721 // insert sections into hiddenSectionSize
1722 QHash<int, int> newHiddenSectionSize; // from logical index to section size
1723 for (int i = 0; i < logicalFirst; ++i)
1724 if (isSectionHidden(i))
1725 newHiddenSectionSize[i] = d->hiddenSectionSize[i];
1726 for (int j = logicalLast + 1; j < d->sectionCount; ++j)
1727 if (isSectionHidden(j))
1728 newHiddenSectionSize[j] = d->hiddenSectionSize[j - insertCount];
1729 d->hiddenSectionSize = newHiddenSectionSize;
1731 d->doDelayedResizeSections();
1732 emit sectionCountChanged(oldCount, count());
1734 // if the new sections were not updated by resizing, we need to update now
1735 if (!d->hasAutoResizeSections())
1736 d->viewport->update();
1740 This slot is called when sections are removed from the \a parent.
1741 \a logicalFirst and \a logicalLast signify where the sections were removed.
1743 If only one section is removed, \a logicalFirst and \a logicalLast will
1747 void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
1748 int logicalFirst, int logicalLast)
1751 Q_UNUSED(logicalFirst);
1752 Q_UNUSED(logicalLast);
1755 void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
1758 const int changeCount = logicalLast - logicalFirst + 1;
1760 // remove sections from hiddenSectionSize
1761 QHash<int, int> newHiddenSectionSize; // from logical index to section size
1762 for (int i = 0; i < logicalFirst; ++i)
1763 if (q->isSectionHidden(i))
1764 newHiddenSectionSize[i] = hiddenSectionSize[i];
1765 for (int j = logicalLast + 1; j < sectionCount; ++j)
1766 if (q->isSectionHidden(j))
1767 newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
1768 hiddenSectionSize = newHiddenSectionSize;
1770 // remove sections from sectionsHidden
1771 if (!sectionHidden.isEmpty()) {
1772 const int newsize = qMin(sectionCount - changeCount, sectionHidden.size());
1773 QBitArray newSectionHidden(newsize);
1774 for (int j = 0, k = 0; j < sectionHidden.size(); ++j) {
1775 const int logical = logicalIndex(j);
1776 if (logical < logicalFirst || logical > logicalLast) {
1777 newSectionHidden[k++] = sectionHidden[j];
1780 sectionHidden = newSectionHidden;
1784 void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
1785 int logicalFirst, int logicalLast)
1789 return; // we only handle changes in the top level
1790 if (qMin(logicalFirst, logicalLast) < 0
1791 || qMax(logicalLast, logicalFirst) >= sectionCount)
1793 int oldCount = q->count();
1794 int changeCount = logicalLast - logicalFirst + 1;
1796 updateHiddenSections(logicalFirst, logicalLast);
1798 if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
1799 //Q_ASSERT(headerSectionCount() == sectionCount);
1800 removeSectionsFromSpans(logicalFirst, logicalLast);
1802 for (int l = logicalLast; l >= logicalFirst; --l) {
1803 int visual = visualIndices.at(l);
1804 for (int v = 0; v < sectionCount; ++v) {
1805 if (v >= logicalIndices.count())
1806 continue; // the section doesn't exist
1808 int logical = logicalIndices.at(v);
1809 --(visualIndices[logical]);
1811 if (logicalIndex(v) > l) // no need to move the positions before l
1812 --(logicalIndices[v]);
1814 logicalIndices.remove(visual);
1815 visualIndices.remove(l);
1816 //Q_ASSERT(headerSectionCount() == sectionCount);
1817 removeSectionsFromSpans(visual, visual);
1819 // ### handle sectionSelection, sectionHidden
1821 sectionCount -= changeCount;
1823 // update sorting column
1824 if (sortIndicatorSection >= logicalFirst) {
1825 if (sortIndicatorSection <= logicalLast)
1826 sortIndicatorSection = -1;
1828 sortIndicatorSection -= changeCount;
1831 // if we only have the last section (the "end" position) left, the header is empty
1832 if (sectionCount <= 0)
1834 invalidateCachedSizeHint();
1835 emit q->sectionCountChanged(oldCount, q->count());
1839 void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
1841 //if there is no row/column we can't have mapping for columns
1842 //because no QModelIndex in the model would be valid
1843 // ### this is far from being bullet-proof and we would need a real system to
1844 // ### map columns or rows persistently
1845 if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
1846 || model->columnCount(root) == 0)
1849 for (int i = 0; i < sectionHidden.count(); ++i)
1850 if (sectionHidden.testBit(i)) // ### note that we are using column or row 0
1851 persistentHiddenSections.append(orientation == Qt::Horizontal
1852 ? model->index(0, logicalIndex(i), root)
1853 : model->index(logicalIndex(i), 0, root));
1856 void QHeaderViewPrivate::_q_layoutChanged()
1860 if (persistentHiddenSections.isEmpty() || modelIsEmpty()) {
1861 if (modelSectionCount() != sectionCount)
1862 q->initializeSections();
1863 persistentHiddenSections.clear();
1867 QBitArray oldSectionHidden = sectionHidden;
1868 bool sectionCountChanged = false;
1870 for (int i = 0; i < persistentHiddenSections.count(); ++i) {
1871 QModelIndex index = persistentHiddenSections.at(i);
1872 if (index.isValid()) {
1873 const int logical = (orientation == Qt::Horizontal
1876 q->setSectionHidden(logical, true);
1877 oldSectionHidden.setBit(logical, false);
1878 } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) {
1879 sectionCountChanged = true;
1883 persistentHiddenSections.clear();
1885 for (int i = 0; i < oldSectionHidden.count(); ++i) {
1886 if (oldSectionHidden.testBit(i))
1887 q->setSectionHidden(i, false);
1890 // the number of sections changed; we need to reread the state of the model
1891 if (sectionCountChanged)
1892 q->initializeSections();
1899 void QHeaderView::initializeSections()
1902 const int oldCount = d->sectionCount;
1903 const int newCount = d->modelSectionCount();
1904 if (newCount <= 0) {
1906 emit sectionCountChanged(oldCount, 0);
1907 } else if (newCount != oldCount) {
1908 const int min = qBound(0, oldCount, newCount - 1);
1909 initializeSections(min, newCount - 1);
1910 if (stretchLastSection()) // we've already gotten the size hint
1911 d->lastSectionSize = sectionSize(logicalIndex(d->sectionCount - 1));
1913 //make sure we update the hidden sections
1914 if (newCount < oldCount)
1915 d->updateHiddenSections(0, newCount-1);
1923 void QHeaderView::initializeSections(int start, int end)
1927 Q_ASSERT(start >= 0);
1930 d->invalidateCachedSizeHint();
1932 if (end + 1 < d->sectionCount) {
1933 int newCount = end + 1;
1934 d->removeSectionsFromSpans(newCount, d->sectionCount);
1935 if (!d->hiddenSectionSize.isEmpty()) {
1936 if (d->sectionCount - newCount > d->hiddenSectionSize.count()) {
1937 for (int i = end + 1; i < d->sectionCount; ++i)
1938 d->hiddenSectionSize.remove(i);
1940 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
1941 while (it != d->hiddenSectionSize.end()) {
1943 it = d->hiddenSectionSize.erase(it);
1951 int oldCount = d->sectionCount;
1952 d->sectionCount = end + 1;
1954 if (!d->logicalIndices.isEmpty()) {
1955 if (oldCount <= d->sectionCount) {
1956 d->logicalIndices.resize(d->sectionCount);
1957 d->visualIndices.resize(d->sectionCount);
1958 for (int i = oldCount; i < d->sectionCount; ++i) {
1959 d->logicalIndices[i] = i;
1960 d->visualIndices[i] = i;
1964 for (int i = 0; i < oldCount; ++i) {
1965 int v = d->logicalIndices.at(i);
1966 if (v < d->sectionCount) {
1967 d->logicalIndices[j] = v;
1968 d->visualIndices[v] = j;
1972 d->logicalIndices.resize(d->sectionCount);
1973 d->visualIndices.resize(d->sectionCount);
1977 if (d->globalResizeMode == Stretch)
1978 d->stretchSections = d->sectionCount;
1979 else if (d->globalResizeMode == ResizeToContents)
1980 d->contentsSections = d->sectionCount;
1981 if (!d->sectionHidden.isEmpty())
1982 d->sectionHidden.resize(d->sectionCount);
1984 if (d->sectionCount > oldCount)
1985 d->createSectionSpan(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
1986 //Q_ASSERT(d->headerLength() == d->length);
1988 if (d->sectionCount != oldCount)
1989 emit sectionCountChanged(oldCount, d->sectionCount);
1990 d->viewport->update();
1997 void QHeaderView::currentChanged(const QModelIndex ¤t, const QModelIndex &old)
2001 if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
2002 if (old.isValid() && old.parent() == d->root)
2003 d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
2004 sectionSize(old.column()), d->viewport->height()));
2005 if (current.isValid() && current.parent() == d->root)
2006 d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
2007 sectionSize(current.column()), d->viewport->height()));
2008 } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
2009 if (old.isValid() && old.parent() == d->root)
2010 d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
2011 d->viewport->width(), sectionSize(old.row())));
2012 if (current.isValid() && current.parent() == d->root)
2013 d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2014 d->viewport->width(), sectionSize(current.row())));
2023 bool QHeaderView::event(QEvent *e)
2026 switch (e->type()) {
2027 case QEvent::HoverEnter: {
2028 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2029 d->hover = logicalIndexAt(he->pos());
2031 updateSection(d->hover);
2034 case QEvent::HoverLeave: {
2036 updateSection(d->hover);
2039 case QEvent::HoverMove: {
2040 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2041 int oldHover = d->hover;
2042 d->hover = logicalIndexAt(he->pos());
2043 if (d->hover != oldHover) {
2045 updateSection(oldHover);
2047 updateSection(d->hover);
2050 case QEvent::Timer: {
2051 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2052 if (te->timerId() == d->delayedResize.timerId()) {
2053 d->delayedResize.stop();
2060 return QAbstractItemView::event(e);
2067 void QHeaderView::paintEvent(QPaintEvent *e)
2074 QPainter painter(d->viewport);
2075 const QPoint offset = d->scrollDelayOffset;
2076 QRect translatedEventRect = e->rect();
2077 translatedEventRect.translate(offset);
2081 if (d->orientation == Qt::Horizontal) {
2082 start = visualIndexAt(translatedEventRect.left());
2083 end = visualIndexAt(translatedEventRect.right());
2085 start = visualIndexAt(translatedEventRect.top());
2086 end = visualIndexAt(translatedEventRect.bottom());
2090 start = (start == -1 ? count() - 1 : start);
2091 end = (end == -1 ? 0 : end);
2093 start = (start == -1 ? 0 : start);
2094 end = (end == -1 ? count() - 1 : end);
2098 start = qMin(start, end);
2099 end = qMax(tmp, end);
2101 d->prepareSectionSelected(); // clear and resize the bit array
2103 QRect currentSectionRect;
2105 const int width = d->viewport->width();
2106 const int height = d->viewport->height();
2107 for (int i = start; i <= end; ++i) {
2108 if (d->isVisualIndexHidden(i))
2111 logical = logicalIndex(i);
2112 if (d->orientation == Qt::Horizontal) {
2113 currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height);
2115 currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical));
2117 currentSectionRect.translate(offset);
2119 QVariant variant = d->model->headerData(logical, d->orientation,
2121 if (variant.isValid() && variant.canConvert<QFont>()) {
2122 QFont sectionFont = qvariant_cast<QFont>(variant);
2123 painter.setFont(sectionFont);
2125 paintSection(&painter, currentSectionRect, logical);
2131 // Paint the area beyond where there are indexes
2133 opt.state |= QStyle::State_Horizontal;
2134 if (currentSectionRect.left() > translatedEventRect.left()) {
2135 opt.rect = QRect(translatedEventRect.left(), 0,
2136 currentSectionRect.left() - translatedEventRect.left(), height);
2137 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2139 } else if (currentSectionRect.right() < translatedEventRect.right()) {
2140 // paint to the right
2141 opt.state |= QStyle::State_Horizontal;
2142 opt.rect = QRect(currentSectionRect.right() + 1, 0,
2143 translatedEventRect.right() - currentSectionRect.right(), height);
2144 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2145 } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2146 // paint the bottom section
2147 opt.state &= ~QStyle::State_Horizontal;
2148 opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2149 width, height - currentSectionRect.bottom() - 1);
2150 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2154 // ### visualize section spans
2155 for (int a = 0, i = 0; i < d->sectionSpans.count(); ++i) {
2156 QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2157 if (d->orientation == Qt::Horizontal)
2158 painter.fillRect(a - d->offset, 0, d->sectionSpans.at(i).size, 4, color);
2160 painter.fillRect(0, a - d->offset, 4, d->sectionSpans.at(i).size, color);
2161 a += d->sectionSpans.at(i).size;
2171 void QHeaderView::mousePressEvent(QMouseEvent *e)
2174 if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2176 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2177 int handle = d->sectionHandleAt(pos);
2178 d->originalSize = -1; // clear the stored original size
2180 d->pressed = logicalIndexAt(pos);
2181 if (d->clickableSections)
2182 emit sectionPressed(d->pressed);
2183 if (d->movableSections) {
2184 d->section = d->target = d->pressed;
2185 if (d->section == -1)
2187 d->state = QHeaderViewPrivate::MoveSection;
2188 d->setupSectionIndicator(d->section, pos);
2189 } else if (d->clickableSections && d->pressed != -1) {
2190 updateSection(d->pressed);
2191 d->state = QHeaderViewPrivate::SelectSections;
2193 } else if (resizeMode(handle) == Interactive) {
2194 d->originalSize = sectionSize(handle);
2195 d->state = QHeaderViewPrivate::ResizeSection;
2196 d->section = handle;
2202 d->clearCascadingSections();
2209 void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2212 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2213 if (pos < 0 && d->state != QHeaderViewPrivate::SelectSections)
2215 if (e->buttons() == Qt::NoButton) {
2216 #if !defined(Q_WS_MAC)
2217 // Under Cocoa, when the mouse button is released, may include an extra
2218 // simulated mouse moved event. The state of the buttons when this event
2219 // is generated is already "no button" and the code below gets executed
2220 // just before the mouseReleaseEvent and resets the state. This prevents
2221 // column dragging from working. So this code is disabled under Cocoa.
2222 d->state = QHeaderViewPrivate::NoState;
2227 case QHeaderViewPrivate::ResizeSection: {
2228 Q_ASSERT(d->originalSize != -1);
2229 if (d->cascadingResizing) {
2230 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2231 int visual = visualIndex(d->section);
2232 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2234 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2235 resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize()));
2240 case QHeaderViewPrivate::MoveSection: {
2241 if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2242 || !d->sectionIndicator->isHidden()) {
2243 int visual = visualIndexAt(pos);
2246 int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2;
2247 int moving = visualIndex(d->section);
2248 if (visual < moving) {
2249 if (pos < posThreshold)
2250 d->target = d->logicalIndex(visual);
2252 d->target = d->logicalIndex(visual + 1);
2253 } else if (visual > moving) {
2254 if (pos > posThreshold)
2255 d->target = d->logicalIndex(visual);
2257 d->target = d->logicalIndex(visual - 1);
2259 d->target = d->section;
2261 d->updateSectionIndicator(d->section, pos);
2265 case QHeaderViewPrivate::SelectSections: {
2266 int logical = logicalIndexAt(qMax(-d->offset, pos));
2267 if (logical == -1 && pos > 0)
2268 logical = d->lastVisibleVisualIndex();
2269 if (logical == d->pressed)
2270 return; // nothing to do
2271 else if (d->pressed != -1)
2272 updateSection(d->pressed);
2273 d->pressed = logical;
2274 if (d->clickableSections && logical != -1) {
2275 emit sectionEntered(d->pressed);
2276 updateSection(d->pressed);
2280 case QHeaderViewPrivate::NoState: {
2281 #ifndef QT_NO_CURSOR
2282 int handle = d->sectionHandleAt(pos);
2283 bool hasCursor = testAttribute(Qt::WA_SetCursor);
2284 if (handle != -1 && (resizeMode(handle) == Interactive)) {
2286 setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2287 } else if (hasCursor) {
2302 void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2305 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2307 case QHeaderViewPrivate::MoveSection:
2308 if (!d->sectionIndicator->isHidden()) { // moving
2309 int from = visualIndex(d->section);
2310 Q_ASSERT(from != -1);
2311 int to = visualIndex(d->target);
2313 moveSection(from, to);
2314 d->section = d->target = -1;
2315 d->updateSectionIndicator(d->section, pos);
2318 case QHeaderViewPrivate::SelectSections:
2319 if (!d->clickableSections) {
2320 int section = logicalIndexAt(pos);
2321 updateSection(section);
2324 case QHeaderViewPrivate::NoState:
2325 if (d->clickableSections) {
2326 int section = logicalIndexAt(pos);
2327 if (section != -1 && section == d->pressed) {
2328 d->flipSortIndicator(section);
2329 emit sectionClicked(section);
2331 if (d->pressed != -1)
2332 updateSection(d->pressed);
2335 case QHeaderViewPrivate::ResizeSection:
2336 d->originalSize = -1;
2337 d->clearCascadingSections();
2342 d->state = QHeaderViewPrivate::NoState;
2350 void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2353 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2354 int handle = d->sectionHandleAt(pos);
2355 if (handle > -1 && resizeMode(handle) == Interactive) {
2356 emit sectionHandleDoubleClicked(handle);
2357 #ifndef QT_NO_CURSOR
2358 Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2359 ? Qt::SplitHCursor : Qt::SplitVCursor;
2360 if (cursor().shape() == splitCursor) {
2361 // signal handlers may have changed the section size
2362 handle = d->sectionHandleAt(pos);
2363 if (!(handle > -1 && resizeMode(handle) == Interactive))
2364 setCursor(Qt::ArrowCursor);
2368 emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2376 bool QHeaderView::viewportEvent(QEvent *e)
2379 switch (e->type()) {
2380 #ifndef QT_NO_TOOLTIP
2381 case QEvent::ToolTip: {
2382 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2383 int logical = logicalIndexAt(he->pos());
2384 if (logical != -1) {
2385 QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2386 if (variant.isValid()) {
2387 QToolTip::showText(he->globalPos(), variant.toString(), this);
2393 #ifndef QT_NO_WHATSTHIS
2394 case QEvent::QueryWhatsThis: {
2395 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2396 int logical = logicalIndexAt(he->pos());
2398 && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2401 case QEvent::WhatsThis: {
2402 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2403 int logical = logicalIndexAt(he->pos());
2404 if (logical != -1) {
2405 QVariant whatsthis = d->model->headerData(logical, d->orientation,
2407 if (whatsthis.isValid()) {
2408 QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2413 #endif // QT_NO_WHATSTHIS
2414 #ifndef QT_NO_STATUSTIP
2415 case QEvent::StatusTip: {
2416 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2417 int logical = logicalIndexAt(he->pos());
2418 if (logical != -1) {
2419 QString statustip = d->model->headerData(logical, d->orientation,
2420 Qt::StatusTipRole).toString();
2421 if (!statustip.isEmpty())
2422 setStatusTip(statustip);
2425 #endif // QT_NO_STATUSTIP
2428 case QEvent::FontChange:
2429 case QEvent::StyleChange:
2430 d->invalidateCachedSizeHint();
2432 emit geometriesChanged();
2434 case QEvent::ContextMenu: {
2435 d->state = QHeaderViewPrivate::NoState;
2436 d->pressed = d->section = d->target = -1;
2437 d->updateSectionIndicator(d->section, -1);
2439 case QEvent::Wheel: {
2440 QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2442 return QApplication::sendEvent(asa->viewport(), e);
2447 return QAbstractItemView::viewportEvent(e);
2451 Paints the section specified by the given \a logicalIndex, using the given
2452 \a painter and \a rect.
2454 Normally, you do not have to call this function.
2457 void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2459 Q_D(const QHeaderView);
2460 if (!rect.isValid())
2462 // get the state of the section
2463 QStyleOptionHeader opt;
2464 initStyleOption(&opt);
2465 QStyle::State state = QStyle::State_None;
2467 state |= QStyle::State_Enabled;
2468 if (window()->isActiveWindow())
2469 state |= QStyle::State_Active;
2470 if (d->clickableSections) {
2471 if (logicalIndex == d->hover)
2472 state |= QStyle::State_MouseOver;
2473 if (logicalIndex == d->pressed)
2474 state |= QStyle::State_Sunken;
2475 else if (d->highlightSelected) {
2476 if (d->sectionIntersectsSelection(logicalIndex))
2477 state |= QStyle::State_On;
2478 if (d->isSectionSelected(logicalIndex))
2479 state |= QStyle::State_Sunken;
2483 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2484 opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2485 ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2487 // setup the style options structure
2488 QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2489 Qt::TextAlignmentRole);
2491 opt.section = logicalIndex;
2493 opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2494 ? Qt::Alignment(textAlignment.toInt())
2495 : d->defaultAlignment);
2497 opt.iconAlignment = Qt::AlignVCenter;
2498 opt.text = d->model->headerData(logicalIndex, d->orientation,
2499 Qt::DisplayRole).toString();
2500 if (d->textElideMode != Qt::ElideNone)
2501 opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);
2503 QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2504 Qt::DecorationRole);
2505 opt.icon = qvariant_cast<QIcon>(variant);
2506 if (opt.icon.isNull())
2507 opt.icon = qvariant_cast<QPixmap>(variant);
2508 QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2509 Qt::ForegroundRole);
2510 if (foregroundBrush.canConvert<QBrush>())
2511 opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2513 QPointF oldBO = painter->brushOrigin();
2514 QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2515 Qt::BackgroundRole);
2516 if (backgroundBrush.canConvert<QBrush>()) {
2517 opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2518 opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2519 painter->setBrushOrigin(opt.rect.topLeft());
2522 // the section position
2523 int visual = visualIndex(logicalIndex);
2524 Q_ASSERT(visual != -1);
2526 opt.position = QStyleOptionHeader::OnlyOneSection;
2527 else if (visual == 0)
2528 opt.position = QStyleOptionHeader::Beginning;
2529 else if (visual == count() - 1)
2530 opt.position = QStyleOptionHeader::End;
2532 opt.position = QStyleOptionHeader::Middle;
2533 opt.orientation = d->orientation;
2534 // the selected position
2535 bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
2536 bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
2537 if (previousSelected && nextSelected)
2538 opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
2539 else if (previousSelected)
2540 opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
2541 else if (nextSelected)
2542 opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
2544 opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
2546 style()->drawControl(QStyle::CE_Header, &opt, painter, this);
2548 painter->setBrushOrigin(oldBO);
2552 Returns the size of the contents of the section specified by the given
2555 \sa defaultSectionSize()
2558 QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
2560 Q_D(const QHeaderView);
2561 Q_ASSERT(logicalIndex >= 0);
2566 QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
2567 if (variant.isValid())
2568 return qvariant_cast<QSize>(variant);
2570 // otherwise use the contents
2571 QStyleOptionHeader opt;
2572 initStyleOption(&opt);
2573 opt.section = logicalIndex;
2574 QVariant var = d->model->headerData(logicalIndex, d->orientation,
2577 if (var.isValid() && var.canConvert<QFont>())
2578 fnt = qvariant_cast<QFont>(var);
2582 opt.fontMetrics = QFontMetrics(fnt);
2583 opt.text = d->model->headerData(logicalIndex, d->orientation,
2584 Qt::DisplayRole).toString();
2585 variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
2586 opt.icon = qvariant_cast<QIcon>(variant);
2587 if (opt.icon.isNull())
2588 opt.icon = qvariant_cast<QPixmap>(variant);
2589 QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
2590 if (isSortIndicatorShown()) {
2591 int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, &opt, this);
2592 if (d->orientation == Qt::Horizontal)
2593 size.rwidth() += size.height() + margin;
2595 size.rheight() += size.width() + margin;
2601 Returns the horizontal offset of the header. This is 0 for vertical
2607 int QHeaderView::horizontalOffset() const
2609 Q_D(const QHeaderView);
2610 if (d->orientation == Qt::Horizontal)
2616 Returns the vertical offset of the header. This is 0 for horizontal
2622 int QHeaderView::verticalOffset() const
2624 Q_D(const QHeaderView);
2625 if (d->orientation == Qt::Vertical)
2635 void QHeaderView::updateGeometries()
2638 d->layoutChildren();
2639 if (d->hasAutoResizeSections())
2640 d->doDelayedResizeSections();
2648 void QHeaderView::scrollContentsBy(int dx, int dy)
2651 d->scrollDirtyRegion(dx, dy);
2658 void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
2661 d->invalidateCachedSizeHint();
2662 if (d->hasAutoResizeSections()) {
2663 bool resizeRequired = d->globalResizeMode == ResizeToContents;
2664 int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
2665 int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
2666 for (int i = first; i <= last && !resizeRequired; ++i)
2667 resizeRequired = (resizeMode(i) == ResizeToContents);
2669 d->doDelayedResizeSections();
2677 Empty implementation because the header doesn't show QModelIndex items.
2679 void QHeaderView::rowsInserted(const QModelIndex &, int, int)
2688 Empty implementation because the header doesn't show QModelIndex items.
2691 QRect QHeaderView::visualRect(const QModelIndex &) const
2700 Empty implementation because the header doesn't show QModelIndex items.
2703 void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
2705 // do nothing - the header only displays sections
2712 Empty implementation because the header doesn't show QModelIndex items.
2715 QModelIndex QHeaderView::indexAt(const QPoint &) const
2717 return QModelIndex();
2724 Empty implementation because the header doesn't show QModelIndex items.
2727 bool QHeaderView::isIndexHidden(const QModelIndex &) const
2729 return true; // the header view has no items, just sections
2736 Empty implementation because the header doesn't show QModelIndex items.
2739 QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
2741 return QModelIndex();
2747 Selects the items in the given \a rect according to the specified
2750 The base class implementation does nothing.
2753 void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
2762 QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
2764 Q_D(const QHeaderView);
2765 const int max = d->modelSectionCount();
2766 if (d->orientation == Qt::Horizontal) {
2769 int rangeLeft, rangeRight;
2771 for (int i = 0; i < selection.count(); ++i) {
2772 QItemSelectionRange r = selection.at(i);
2773 if (r.parent().isValid() || !r.isValid())
2774 continue; // we only know about toplevel items and we don't want invalid ranges
2775 // FIXME an item inside the range may be the leftmost or rightmost
2776 rangeLeft = visualIndex(r.left());
2777 if (rangeLeft == -1) // in some cases users may change the selections
2778 continue; // before we have a chance to do the layout
2779 rangeRight = visualIndex(r.right());
2780 if (rangeRight == -1) // in some cases users may change the selections
2781 continue; // before we have a chance to do the layout
2782 if (rangeLeft < left)
2784 if (rangeRight > right)
2788 int logicalLeft = logicalIndex(left);
2789 int logicalRight = logicalIndex(right);
2791 if (logicalLeft < 0 || logicalLeft >= count() ||
2792 logicalRight < 0 || logicalRight >= count())
2795 int leftPos = sectionViewportPosition(logicalLeft);
2796 int rightPos = sectionViewportPosition(logicalRight);
2797 rightPos += sectionSize(logicalRight);
2798 return QRect(leftPos, 0, rightPos - leftPos, height());
2800 // orientation() == Qt::Vertical
2803 int rangeTop, rangeBottom;
2805 for (int i = 0; i < selection.count(); ++i) {
2806 QItemSelectionRange r = selection.at(i);
2807 if (r.parent().isValid() || !r.isValid())
2808 continue; // we only know about toplevel items
2809 // FIXME an item inside the range may be the leftmost or rightmost
2810 rangeTop = visualIndex(r.top());
2811 if (rangeTop == -1) // in some cases users may change the selections
2812 continue; // before we have a chance to do the layout
2813 rangeBottom = visualIndex(r.bottom());
2814 if (rangeBottom == -1) // in some cases users may change the selections
2815 continue; // before we have a chance to do the layout
2818 if (rangeBottom > bottom)
2819 bottom = rangeBottom;
2822 int logicalTop = logicalIndex(top);
2823 int logicalBottom = logicalIndex(bottom);
2825 if (logicalTop == -1 || logicalBottom == -1)
2828 int topPos = sectionViewportPosition(logicalTop);
2829 int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
2831 return QRect(0, topPos, width(), bottomPos - topPos);
2835 // private implementation
2837 int QHeaderViewPrivate::sectionHandleAt(int position)
2840 int visual = q->visualIndexAt(position);
2843 int log = logicalIndex(visual);
2844 int pos = q->sectionViewportPosition(log);
2845 int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, q);
2847 bool atLeft = position < pos + grip;
2848 bool atRight = (position > pos + q->sectionSize(log) - grip);
2850 qSwap(atLeft, atRight);
2853 //grip at the beginning of the section
2854 while(visual > -1) {
2855 int logical = q->logicalIndex(--visual);
2856 if (!q->isSectionHidden(logical))
2859 } else if (atRight) {
2860 //grip at the end of the section
2866 void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
2869 if (!sectionIndicator) {
2870 sectionIndicator = new QLabel(viewport);
2874 int p = q->sectionViewportPosition(section);
2875 if (orientation == Qt::Horizontal) {
2876 w = q->sectionSize(section);
2877 h = viewport->height();
2879 w = viewport->width();
2880 h = q->sectionSize(section);
2882 sectionIndicator->resize(w, h);
2885 pm.fill(QColor(0, 0, 0, 45));
2886 QRect rect(0, 0, w, h);
2888 QPainter painter(&pm);
2889 painter.setOpacity(0.75);
2890 q->paintSection(&painter, rect, section);
2893 sectionIndicator->setPixmap(pm);
2894 sectionIndicatorOffset = position - qMax(p, 0);
2897 void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
2899 if (!sectionIndicator)
2902 if (section == -1 || target == -1) {
2903 sectionIndicator->hide();
2907 if (orientation == Qt::Horizontal)
2908 sectionIndicator->move(position - sectionIndicatorOffset, 0);
2910 sectionIndicator->move(0, position - sectionIndicatorOffset);
2912 sectionIndicator->show();
2916 Initialize \a option with the values from this QHeaderView. This method is
2917 useful for subclasses when they need a QStyleOptionHeader, but do not want
2918 to fill in all the information themselves.
2920 \sa QStyleOption::initFrom()
2922 void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
2924 Q_D(const QHeaderView);
2925 option->initFrom(this);
2926 option->state = QStyle::State_None | QStyle::State_Raised;
2927 option->orientation = d->orientation;
2928 if (d->orientation == Qt::Horizontal)
2929 option->state |= QStyle::State_Horizontal;
2931 option->state |= QStyle::State_Enabled;
2932 option->section = 0;
2935 bool QHeaderViewPrivate::isSectionSelected(int section) const
2937 int i = section * 2;
2938 if (i < 0 || i >= sectionSelected.count())
2940 if (sectionSelected.testBit(i)) // if the value was cached
2941 return sectionSelected.testBit(i + 1);
2943 if (orientation == Qt::Horizontal)
2944 s = isColumnSelected(section);
2946 s = isRowSelected(section);
2947 sectionSelected.setBit(i + 1, s); // selection state
2948 sectionSelected.setBit(i, true); // cache state
2954 Returns the last visible (ie. not hidden) visual index
2956 int QHeaderViewPrivate::lastVisibleVisualIndex() const
2958 Q_Q(const QHeaderView);
2959 for (int visual = q->count()-1; visual >= 0; --visual) {
2960 if (!q->isSectionHidden(q->logicalIndex(visual)))
2964 //default value if no section is actually visible
2970 Go through and resize all of the sections applying stretchLastSection,
2971 manualy stretches, sizes, and useGlobalMode.
2973 The different resize modes are:
2974 Interactive - the user decides the size
2975 Stretch - take up whatever space is left
2976 Fixed - the size is set programmatically outside the header
2977 ResizeToContentes - the size is set based on the contents of the row or column in the parent view
2979 The resize mode will not affect the last section if stretchLastSection is true.
2981 void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
2984 //stop the timer in case it is delayed
2985 delayedResize.stop();
2987 executePostedLayout();
2988 if (sectionCount == 0)
2991 if (resizeRecursionBlock)
2993 resizeRecursionBlock = true;
2995 invalidateCachedSizeHint();
2997 const int lastVisibleSection = lastVisibleVisualIndex();
2999 // find stretchLastSection if we have it
3000 int stretchSection = -1;
3001 if (stretchLastSection && !useGlobalMode)
3002 stretchSection = lastVisibleVisualIndex();
3004 // count up the number of strected sections and how much space left for them
3005 int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
3006 int numberOfStretchedSections = 0;
3007 QList<int> section_sizes;
3008 for (int i = 0; i < sectionCount; ++i) {
3009 if (isVisualIndexHidden(i))
3012 QHeaderView::ResizeMode resizeMode;
3013 if (useGlobalMode && (i != stretchSection))
3014 resizeMode = globalMode;
3016 resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i));
3018 if (resizeMode == QHeaderView::Stretch) {
3019 ++numberOfStretchedSections;
3020 section_sizes.append(headerSectionSize(i));
3024 // because it isn't stretch, determine its width and remove that from lengthToStrech
3025 int sectionSize = 0;
3026 if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
3027 sectionSize = headerSectionSize(i);
3028 } else { // resizeMode == QHeaderView::ResizeToContents
3029 int logicalIndex = q->logicalIndex(i);
3030 sectionSize = qMax(viewSectionSizeHint(logicalIndex),
3031 q->sectionSizeHint(logicalIndex));
3033 section_sizes.append(sectionSize);
3034 lengthToStrech -= sectionSize;
3037 // calculate the new length for all of the stretched sections
3038 int stretchSectionLength = -1;
3039 int pixelReminder = 0;
3040 if (numberOfStretchedSections > 0 && lengthToStrech > 0) { // we have room to stretch in
3041 int hintLengthForEveryStretchedSection = lengthToStrech / numberOfStretchedSections;
3042 stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
3043 pixelReminder = lengthToStrech % numberOfStretchedSections;
3046 int spanStartSection = 0;
3047 int previousSectionLength = 0;
3049 QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
3051 // resize each section along the total length
3052 for (int i = 0; i < sectionCount; ++i) {
3053 int oldSectionLength = headerSectionSize(i);
3054 int newSectionLength = -1;
3055 QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
3057 if (isVisualIndexHidden(i)) {
3058 newSectionLength = 0;
3060 QHeaderView::ResizeMode resizeMode;
3062 resizeMode = globalMode;
3064 resizeMode = (i == stretchSection
3065 ? QHeaderView::Stretch
3066 : newSectionResizeMode);
3067 if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
3068 if (i == lastVisibleSection)
3069 newSectionLength = qMax(stretchSectionLength, lastSectionSize);
3071 newSectionLength = stretchSectionLength;
3072 if (pixelReminder > 0) {
3073 newSectionLength += 1;
3076 section_sizes.removeFirst();
3078 newSectionLength = section_sizes.front();
3079 section_sizes.removeFirst();
3083 //Q_ASSERT(newSectionLength > 0);
3084 if ((previousSectionResizeMode != newSectionResizeMode
3085 || previousSectionLength != newSectionLength) && i > 0) {
3086 int spanLength = (i - spanStartSection) * previousSectionLength;
3087 createSectionSpan(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
3088 //Q_ASSERT(headerLength() == length);
3089 spanStartSection = i;
3092 if (newSectionLength != oldSectionLength)
3093 emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
3095 previousSectionLength = newSectionLength;
3096 previousSectionResizeMode = newSectionResizeMode;
3099 createSectionSpan(spanStartSection, sectionCount - 1,
3100 (sectionCount - spanStartSection) * previousSectionLength,
3101 previousSectionResizeMode);
3102 //Q_ASSERT(headerLength() == length);
3103 resizeRecursionBlock = false;
3107 void QHeaderViewPrivate::createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode)
3109 // ### the code for merging spans does not merge at all opertuneties
3110 // ### what if the number of sections is reduced ?
3112 SectionSpan span(size, (end - start) + 1, mode);
3113 int start_section = 0;
3115 int initial_section_count = headerSectionCount(); // ### debug code
3118 QList<int> spansToRemove;
3119 for (int i = 0; i < sectionSpans.count(); ++i) {
3120 int end_section = start_section + sectionSpans.at(i).count - 1;
3121 int section_count = sectionSpans.at(i).count;
3122 if (start <= start_section && end > end_section) {
3123 // the existing span is entirely coveded by the new span
3124 spansToRemove.append(i);
3125 } else if (start < start_section && end >= end_section) {
3126 // the existing span is entirely coveded by the new span
3127 spansToRemove.append(i);
3128 } else if (start == start_section && end == end_section) {
3129 // the new span is covered by an existin span
3130 length -= sectionSpans.at(i).size;
3132 sectionSpans[i].size = size;
3133 sectionSpans[i].resizeMode = mode;
3134 // ### check if we can merge the section with any of its neighbours
3135 removeSpans(spansToRemove);
3136 Q_ASSERT(initial_section_count == headerSectionCount());
3138 } else if (start > start_section && end < end_section) {
3139 if (sectionSpans.at(i).sectionSize() == span.sectionSize()
3140 && sectionSpans.at(i).resizeMode == span.resizeMode) {
3141 Q_ASSERT(initial_section_count == headerSectionCount());
3144 // the new span is in the middle of the old span, so we have to split it
3145 length -= sectionSpans.at(i).size;
3146 int section_size = sectionSpans.at(i).sectionSize();
3148 int span_count = sectionSpans.at(i).count;
3150 QHeaderView::ResizeMode span_mode = sectionSpans.at(i).resizeMode;
3152 int first_span_count = start - start_section;
3153 int first_span_size = section_size * first_span_count;
3154 sectionSpans[i].count = first_span_count;
3155 sectionSpans[i].size = first_span_size;
3156 sectionSpans[i].resizeMode = span_mode;
3157 length += first_span_size;
3158 // middle span (the new span)
3160 int mid_span_count = span.count;
3162 int mid_span_size = span.size;
3163 sectionSpans.insert(i + 1, span);
3164 length += mid_span_size;
3166 int last_span_count = end_section - end;
3167 int last_span_size = section_size * last_span_count;
3168 sectionSpans.insert(i + 2, SectionSpan(last_span_size, last_span_count, span_mode));
3169 length += last_span_size;
3170 Q_ASSERT(span_count == first_span_count + mid_span_count + last_span_count);
3171 removeSpans(spansToRemove);
3172 Q_ASSERT(initial_section_count == headerSectionCount());
3174 } else if (start > start_section && start <= end_section && end >= end_section) {
3175 // the new span covers the last part of the existing span
3176 length -= sectionSpans.at(i).size;
3177 int removed_count = (end_section - start + 1);
3178 int span_count = sectionSpans.at(i).count - removed_count;
3179 int section_size = sectionSpans.at(i).sectionSize();
3180 int span_size = section_size * span_count;
3181 sectionSpans[i].count = span_count;
3182 sectionSpans[i].size = span_size;
3183 length += span_size;
3184 if (end == end_section) {
3185 sectionSpans.insert(i + 1, span); // insert after
3186 length += span.size;
3187 removeSpans(spansToRemove);
3188 Q_ASSERT(initial_section_count == headerSectionCount());
3191 } else if (end < end_section && end >= start_section && start <= start_section) {
3192 // the new span covers the first part of the existing span
3193 length -= sectionSpans.at(i).size;
3194 int removed_count = (end - start_section + 1);
3195 int section_size = sectionSpans.at(i).sectionSize();
3196 int span_count = sectionSpans.at(i).count - removed_count;
3197 int span_size = section_size * span_count;
3198 sectionSpans[i].count = span_count;
3199 sectionSpans[i].size = span_size;
3200 length += span_size;
3201 sectionSpans.insert(i, span); // insert before
3202 length += span.size;
3203 removeSpans(spansToRemove);
3204 Q_ASSERT(initial_section_count == headerSectionCount());
3207 start_section += section_count;
3210 // ### adding and removing _ sections_ in addition to spans
3211 // ### add some more checks here
3213 if (spansToRemove.isEmpty()) {
3214 if (!sectionSpans.isEmpty()
3215 && sectionSpans.last().sectionSize() == span.sectionSize()
3216 && sectionSpans.last().resizeMode == span.resizeMode) {
3217 length += span.size;
3218 int last = sectionSpans.count() - 1;
3219 sectionSpans[last].count += span.count;
3220 sectionSpans[last].size += span.size;
3221 sectionSpans[last].resizeMode = span.resizeMode;
3223 length += span.size;
3224 sectionSpans.append(span);
3227 removeSpans(spansToRemove);
3228 length += span.size;
3229 sectionSpans.insert(spansToRemove.first(), span);
3230 //Q_ASSERT(initial_section_count == headerSectionCount());
3234 void QHeaderViewPrivate::removeSectionsFromSpans(int start, int end)
3237 int start_section = 0;
3238 QList<int> spansToRemove;
3239 for (int i = 0; i < sectionSpans.count(); ++i) {
3240 int end_section = start_section + sectionSpans.at(i).count - 1;
3241 int section_size = sectionSpans.at(i).sectionSize();
3242 int section_count = sectionSpans.at(i).count;
3243 if (start <= start_section && end >= end_section) {
3244 // the change covers the entire span
3245 spansToRemove.append(i);
3246 if (end == end_section)
3248 } else if (start > start_section && end < end_section) {
3249 // all the removed sections are inside the span
3250 int change = (end - start + 1);
3251 sectionSpans[i].count -= change;
3252 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3253 length -= (change * section_size);
3255 } else if (start >= start_section && start <= end_section) {
3256 // the some of the removed sections are inside the span,at the end
3257 int change = qMin(end_section - start + 1, end - start + 1);
3258 sectionSpans[i].count -= change;
3259 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3261 length -= (change * section_size);
3262 // the change affects several spans
3263 } else if (end >= start_section && end <= end_section) {
3264 // the some of the removed sections are inside the span, at the beginning
3265 int change = qMin((end - start_section + 1), end - start + 1);
3266 sectionSpans[i].count -= change;
3267 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3268 length -= (change * section_size);
3271 start_section += section_count;
3274 for (int i = spansToRemove.count() - 1; i >= 0; --i) {
3275 int s = spansToRemove.at(i);
3276 length -= sectionSpans.at(s).size;
3277 sectionSpans.remove(s);
3278 // ### merge remaining spans
3282 void QHeaderViewPrivate::clear()
3284 if (state != NoClear) {
3287 visualIndices.clear();
3288 logicalIndices.clear();
3289 sectionSelected.clear();
3290 sectionHidden.clear();
3291 hiddenSectionSize.clear();
3292 sectionSpans.clear();
3293 invalidateCachedSizeHint();
3297 void QHeaderViewPrivate::flipSortIndicator(int section)
3300 Qt::SortOrder sortOrder;
3301 if (sortIndicatorSection == section) {
3302 sortOrder = (sortIndicatorOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder;
3304 const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole);
3305 if (value.canConvert(QVariant::Int))
3306 sortOrder = static_cast<Qt::SortOrder>(value.toInt());
3308 sortOrder = Qt::AscendingOrder;
3310 q->setSortIndicator(section, sortOrder);
3313 void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
3316 const int minimumSize = q->minimumSectionSize();
3317 const int oldSize = headerSectionSize(visual);
3318 int delta = newSize - oldSize;
3320 if (delta > 0) { // larger
3321 bool sectionResized = false;
3323 // restore old section sizes
3324 for (int i = firstCascadingSection; i < visual; ++i) {
3325 if (cascadingSectionSize.contains(i)) {
3326 int currentSectionSize = headerSectionSize(i);
3327 int originalSectionSize = cascadingSectionSize.value(i);
3328 if (currentSectionSize < originalSectionSize) {
3329 int newSectionSize = currentSectionSize + delta;
3330 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3331 if (newSectionSize >= originalSectionSize && false)
3332 cascadingSectionSize.remove(i); // the section is now restored
3333 sectionResized = true;
3340 // resize the section
3341 if (!sectionResized) {
3342 newSize = qMax(newSize, minimumSize);
3343 if (oldSize != newSize)
3344 resizeSectionSpan(visual, oldSize, newSize);
3347 // cascade the section size change
3348 for (int i = visual + 1; i < sectionCount; ++i) {
3349 if (!sectionIsCascadable(i))
3351 int currentSectionSize = headerSectionSize(i);
3352 if (currentSectionSize <= minimumSize)
3354 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3355 //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
3356 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3357 saveCascadingSectionSize(i, currentSectionSize);
3358 delta = delta - (currentSectionSize - newSectionSize);
3359 //qDebug() << "new delta" << delta;
3360 //if (newSectionSize != minimumSize)
3365 bool sectionResized = false;
3367 // restore old section sizes
3368 for (int i = lastCascadingSection; i > visual; --i) {
3369 if (!cascadingSectionSize.contains(i))
3371 int currentSectionSize = headerSectionSize(i);
3372 int originalSectionSize = cascadingSectionSize.value(i);
3373 if (currentSectionSize >= originalSectionSize)
3375 int newSectionSize = currentSectionSize - delta;
3376 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3377 if (newSectionSize >= originalSectionSize && false) {
3378 //qDebug() << "section" << i << "restored to" << originalSectionSize;
3379 cascadingSectionSize.remove(i); // the section is now restored
3381 sectionResized = true;
3385 // resize the section
3386 resizeSectionSpan(visual, oldSize, qMax(newSize, minimumSize));
3388 // cascade the section size change
3389 if (delta < 0 && newSize < minimumSize) {
3390 for (int i = visual - 1; i >= 0; --i) {
3391 if (!sectionIsCascadable(i))
3393 int sectionSize = headerSectionSize(i);
3394 if (sectionSize <= minimumSize)
3396 resizeSectionSpan(i, sectionSize, qMax(sectionSize + delta, minimumSize));
3397 saveCascadingSectionSize(i, sectionSize);
3402 // let the next section get the space from the resized section
3403 if (!sectionResized) {
3404 for (int i = visual + 1; i < sectionCount; ++i) {
3405 if (!sectionIsCascadable(i))
3407 int currentSectionSize = headerSectionSize(i);
3408 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3409 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3415 if (hasAutoResizeSections())
3416 doDelayedResizeSections();
3421 void QHeaderViewPrivate::setDefaultSectionSize(int size)
3424 defaultSectionSize = size;
3425 int currentVisualIndex = 0;
3426 for (int i = 0; i < sectionSpans.count(); ++i) {
3427 QHeaderViewPrivate::SectionSpan &span = sectionSpans[i];
3428 if (span.size > 0) {
3429 //we resize it if it is not hidden (ie size > 0)
3430 const int newSize = span.count * size;
3431 if (newSize != span.size) {
3432 length += newSize - span.size; //the whole length is changed
3433 const int oldSectionSize = span.sectionSize();
3434 span.size = span.count * size;
3435 for (int i = currentVisualIndex; i < currentVisualIndex + span.count; ++i) {
3436 emit q->sectionResized(logicalIndex(i), oldSectionSize, size);
3440 currentVisualIndex += span.count;
3444 void QHeaderViewPrivate::resizeSectionSpan(int visualIndex, int oldSize, int newSize)
3447 QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
3448 createSectionSpan(visualIndex, visualIndex, newSize, mode);
3449 emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
3452 int QHeaderViewPrivate::headerSectionSize(int visual) const
3454 // ### stupid iteration
3455 int section_start = 0;
3456 const int sectionSpansCount = sectionSpans.count();
3457 for (int i = 0; i < sectionSpansCount; ++i) {
3458 const QHeaderViewPrivate::SectionSpan ¤tSection = sectionSpans.at(i);
3459 int section_end = section_start + currentSection.count - 1;
3460 if (visual >= section_start && visual <= section_end)
3461 return currentSection.sectionSize();
3462 section_start = section_end + 1;
3467 int QHeaderViewPrivate::headerSectionPosition(int visual) const
3469 // ### stupid iteration
3470 int section_start = 0;
3471 int span_position = 0;
3472 const int sectionSpansCount = sectionSpans.count();
3473 for (int i = 0; i < sectionSpansCount; ++i) {
3474 const QHeaderViewPrivate::SectionSpan ¤tSection = sectionSpans.at(i);
3475 int section_end = section_start + currentSection.count - 1;
3476 if (visual >= section_start && visual <= section_end)
3477 return span_position + (visual - section_start) * currentSection.sectionSize();
3478 section_start = section_end + 1;
3479 span_position += currentSection.size;
3484 int QHeaderViewPrivate::headerVisualIndexAt(int position) const
3486 // ### stupid iteration
3487 int span_start_section = 0;
3488 int span_position = 0;
3489 const int sectionSpansCount = sectionSpans.count();
3490 for (int i = 0; i < sectionSpansCount; ++i) {
3491 const QHeaderViewPrivate::SectionSpan ¤tSection = sectionSpans.at(i);
3492 int next_span_start_section = span_start_section + currentSection.count;
3493 int next_span_position = span_position + currentSection.size;
3494 if (position == span_position && currentSection.size > 0)
3495 return span_start_section;
3496 if (position > span_position && position < next_span_position) {
3497 int position_in_span = position - span_position;
3498 return span_start_section + (position_in_span / currentSection.sectionSize());
3500 span_start_section = next_span_start_section;
3501 span_position = next_span_position;
3506 void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
3508 int size = headerSectionSize(visual);
3509 createSectionSpan(visual, visual, size, mode);
3512 QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
3514 int span = sectionSpanIndex(visual);
3516 return globalResizeMode;
3517 return sectionSpans.at(span).resizeMode;
3520 void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
3522 globalResizeMode = mode;
3523 for (int i = 0; i < sectionSpans.count(); ++i)
3524 sectionSpans[i].resizeMode = mode;
3527 int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
3529 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) {
3530 return (orientation == Qt::Horizontal
3531 ? view->sizeHintForColumn(logical)
3532 : view->sizeHintForRow(logical));
3537 int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
3539 if (hiddenSectionSize.count() > 0) {
3540 int adjustedVisualIndex = visualIndex;
3541 int currentVisualIndex = 0;
3542 for (int i = 0; i < sectionSpans.count(); ++i) {
3543 if (sectionSpans.at(i).size == 0)
3544 adjustedVisualIndex += sectionSpans.at(i).count;
3546 currentVisualIndex += sectionSpans.at(i).count;
3547 if (currentVisualIndex >= visualIndex)
3550 visualIndex = adjustedVisualIndex;