summaryrefslogtreecommitdiffstats
path: root/src/gui/itemviews/qheaderview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/itemviews/qheaderview.cpp')
-rw-r--r--src/gui/itemviews/qheaderview.cpp3558
1 files changed, 3558 insertions, 0 deletions
diff --git a/src/gui/itemviews/qheaderview.cpp b/src/gui/itemviews/qheaderview.cpp
new file mode 100644
index 0000000..5bd82d4
--- /dev/null
+++ b/src/gui/itemviews/qheaderview.cpp
@@ -0,0 +1,3558 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qheaderview.h"
+
+#ifndef QT_NO_ITEMVIEWS
+#include <qbitarray.h>
+#include <qbrush.h>
+#include <qdebug.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qscrollbar.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qstyle.h>
+#include <qstyleoption.h>
+#include <qvector.h>
+#include <qapplication.h>
+#include <qvarlengtharray.h>
+#include <qabstractitemdelegate.h>
+#include <qvariant.h>
+#include <private/qheaderview_p.h>
+#include <private/qabstractitemmodel_p.h>
+
+#ifndef QT_NO_DATASTREAM
+#include <qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionSpan &span)
+{
+ span.write(out);
+ return out;
+}
+
+QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionSpan &span)
+{
+ span.read(in);
+ return in;
+}
+#endif
+
+
+/*!
+ \class QHeaderView
+
+ \brief The QHeaderView class provides a header row or header column for
+ item views.
+
+ \ingroup model-view
+ \mainclass
+
+ A QHeaderView displays the headers used in item views such as the
+ QTableView and QTreeView classes. It takes the place of Qt3's \c QHeader
+ class previously used for the same purpose, but uses the Qt's model/view
+ architecture for consistency with the item view classes.
+
+ The QHeaderView class is one of the \l{Model/View Classes} and is part of
+ Qt's \l{Model/View Programming}{model/view framework}.
+
+ The header gets the data for each section from the model using the
+ QAbstractItemModel::headerData() function. You can set the data by using
+ QAbstractItemModel::setHeaderData().
+
+ Each header has an orientation() and a number of sections, given by the
+ count() function. A section refers to a part of the header - either a row
+ or a column, depending on the orientation.
+
+ Sections can be moved and resized using moveSection() and resizeSection();
+ they can also be hidden and shown with hideSection() and showSection().
+
+ Each section of a header is described by a section ID, specified by its
+ section(), and can be located at a particular visualIndex() in the header.
+ A section can have a sort indicator set with setSortIndicator(); this
+ indicates whether the items in the associated item view will be sorted in
+ the order given by the section.
+
+ For a horizontal header the section is equivalent to a column in the model,
+ and for a vertical header the section is equivalent to a row in the model.
+
+ \section1 Moving Header Sections
+
+ A header can be fixed in place, or made movable with setMovable(). It can
+ be made clickable with setClickable(), and has resizing behavior in
+ accordance with setResizeMode().
+
+ \note Double-clicking on a header to resize a section only applies for
+ visible rows.
+
+ A header will emit sectionMoved() if the user moves a section,
+ sectionResized() if the user resizes a section, and sectionClicked() as
+ well as sectionHandleDoubleClicked() in response to mouse clicks. A header
+ will also emit sectionCountChanged() and sectionAutoResize().
+
+ You can identify a section using the logicalIndex() and logicalIndexAt()
+ functions, or by its index position, using the visualIndex() and
+ visualIndexAt() functions. The visual index will change if a section is
+ moved, but the logical index will not change.
+
+ \section1 Appearance
+
+ QTableWidget and QTableView create default headers. If you want
+ the headers to be visible, you can use \l{QFrame::}{setVisible()}.
+
+ Not all \l{Qt::}{ItemDataRole}s will have an effect on a
+ QHeaderView. If you need to draw other roles, you can subclass
+ QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
+ QHeaderView respects the following item data roles:
+ \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
+ \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
+ \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
+
+ \note Each header renders the data for each section itself, and does not
+ rely on a delegate. As a result, calling a header's setItemDelegate()
+ function will have no effect.
+
+ \sa {Model/View Programming}, QListView, QTableView, QTreeView
+*/
+
+/*!
+ \enum QHeaderView::ResizeMode
+
+ The resize mode specifies the behavior of the header sections. It can be
+ set on the entire header view or on individual sections using
+ setResizeMode().
+
+ \value Interactive The user can resize the section. The section can also be
+ resized programmatically using resizeSection(). The section size
+ defaults to \l defaultSectionSize. (See also
+ \l cascadingSectionResizes.)
+
+ \value Fixed The user cannot resize the section. The section can only be
+ resized programmatically using resizeSection(). The section size
+ defaults to \l defaultSectionSize.
+
+ \value Stretch QHeaderView will automatically resize the section to fill
+ the available space. The size cannot be changed by the user or
+ programmatically.
+
+ \value ResizeToContents QHeaderView will automatically resize the section
+ to its optimal size based on the contents of the entire column or
+ row. The size cannot be changed by the user or programmatically.
+ (This value was introduced in 4.2)
+
+ The following values are obsolete:
+ \value Custom Use Fixed instead.
+
+ \sa setResizeMode() stretchLastSection minimumSectionSize
+*/
+
+/*!
+ \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
+ int newVisualIndex)
+
+ This signal is emitted when a section is moved. The section's logical index
+ is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
+ the new index position by \a newVisualIndex.
+
+ \sa moveSection()
+*/
+
+/*!
+ \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
+ int newSize)
+
+ This signal is emitted when a section is resized. The section's logical
+ number is specified by \a logicalIndex, the old size by \a oldSize, and the
+ new size by \a newSize.
+
+ \sa resizeSection()
+*/
+
+/*!
+ \fn void QHeaderView::sectionPressed(int logicalIndex)
+
+ This signal is emitted when a section is pressed. The section's logical
+ index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sectionClicked(int logicalIndex)
+
+ This signal is emitted when a section is clicked. The section's logical
+ index is specified by \a logicalIndex.
+
+ Note that the sectionPressed signal will also be emitted.
+
+ \sa setClickable(), sectionPressed()
+*/
+
+/*!
+ \fn void QHeaderView::sectionEntered(int logicalIndex)
+ \since 4.3
+
+ This signal is emitted when the cursor moves over the section and the left
+ mouse button is pressed. The section's logical index is specified by
+ \a logicalIndex.
+
+ \sa setClickable(), sectionPressed()
+*/
+
+/*!
+ \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
+
+ This signal is emitted when a section is double-clicked. The section's
+ logical index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
+
+ This signal is emitted when the number of sections changes, i.e., when
+ sections are added or deleted. The original count is specified by
+ \a oldCount, and the new count by \a newCount.
+
+ \sa count(), length(), headerDataChanged()
+*/
+
+/*!
+ \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
+
+ This signal is emitted when a section is double-clicked. The section's
+ logical index is specified by \a logicalIndex.
+
+ \sa setClickable()
+*/
+
+/*!
+ \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
+ Qt::SortOrder order)
+ \since 4.3
+
+ This signal is emitted when the section containing the sort indicator or
+ the order indicated is changed. The section's logical index is specified
+ by \a logicalIndex and the sort order is specified by \a order.
+
+ \sa setSortIndicator()
+*/
+
+/*!
+ \fn void QHeaderView::sectionAutoResize(int logicalIndex,
+ QHeaderView::ResizeMode mode)
+
+ This signal is emitted when a section is automatically resized. The
+ section's logical index is specified by \a logicalIndex, and the resize
+ mode by \a mode.
+
+ \sa setResizeMode(), stretchLastSection()
+*/
+// ### Qt 5: change to sectionAutoResized()
+
+/*!
+ \fn void QHeaderView::geometriesChanged()
+ \since 4.2
+
+ This signal is emitted when the header's geometries have changed.
+*/
+
+/*!
+ \property QHeaderView::highlightSections
+ \brief whether the sections containing selected items are highlighted
+
+ By default, this property is false.
+*/
+
+/*!
+ Creates a new generic header with the given \a orientation and \a parent.
+*/
+QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
+ : QAbstractItemView(*new QHeaderViewPrivate, parent)
+{
+ Q_D(QHeaderView);
+ d->setDefaultValues(orientation);
+ initialize();
+}
+
+/*!
+ \internal
+*/
+QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
+ Qt::Orientation orientation, QWidget *parent)
+ : QAbstractItemView(dd, parent)
+{
+ Q_D(QHeaderView);
+ d->setDefaultValues(orientation);
+ initialize();
+}
+
+/*!
+ Destroys the header.
+*/
+
+QHeaderView::~QHeaderView()
+{
+}
+
+/*!
+ \internal
+*/
+void QHeaderView::initialize()
+{
+ Q_D(QHeaderView);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setFrameStyle(NoFrame);
+ setFocusPolicy(Qt::NoFocus);
+ d->viewport->setMouseTracking(true);
+ d->viewport->setBackgroundRole(QPalette::Button);
+ d->textElideMode = Qt::ElideNone;
+ delete d->itemDelegate;
+}
+
+/*!
+ \reimp
+*/
+void QHeaderView::setModel(QAbstractItemModel *model)
+{
+ if (model == this->model())
+ return;
+ Q_D(QHeaderView);
+ if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ if (d->orientation == Qt::Horizontal) {
+ QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ } else {
+ QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ }
+ QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
+ QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ }
+
+ if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
+ if (d->orientation == Qt::Horizontal) {
+ QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ } else {
+ QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(sectionsInserted(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
+ }
+ QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
+ this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
+ QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
+ this, SLOT(_q_layoutAboutToBeChanged()));
+ }
+
+ d->state = QHeaderViewPrivate::NoClear;
+ QAbstractItemView::setModel(model);
+ d->state = QHeaderViewPrivate::NoState;
+
+ // Users want to set sizes and modes before the widget is shown.
+ // Thus, we have to initialize when the model is set,
+ // and not lazily like we do in the other views.
+ initializeSections();
+}
+
+/*!
+ Returns the orientation of the header.
+
+ \sa Qt::Orientation
+*/
+
+Qt::Orientation QHeaderView::orientation() const
+{
+ Q_D(const QHeaderView);
+ return d->orientation;
+}
+
+/*!
+ Returns the offset of the header: this is the header's left-most (or
+ top-most for vertical headers) visible pixel.
+
+ \sa setOffset()
+*/
+
+int QHeaderView::offset() const
+{
+ Q_D(const QHeaderView);
+ return d->offset;
+}
+
+/*!
+ \fn void QHeaderView::setOffset(int offset)
+
+ Sets the header's offset to \a offset.
+
+ \sa offset(), length()
+*/
+
+void QHeaderView::setOffset(int newOffset)
+{
+ Q_D(QHeaderView);
+ if (d->offset == (int)newOffset)
+ return;
+ int ndelta = d->offset - newOffset;
+ d->offset = newOffset;
+ if (d->orientation == Qt::Horizontal)
+ d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
+ else
+ d->viewport->scroll(0, ndelta);
+ if (d->state == QHeaderViewPrivate::ResizeSection) {
+ QPoint cursorPos = QCursor::pos();
+ if (d->orientation == Qt::Horizontal)
+ QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
+ else
+ QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
+ d->firstPos += ndelta;
+ d->lastPos += ndelta;
+ }
+}
+
+/*!
+ \since 4.2
+ Sets the offset to the start of the section at the given \a visualIndex.
+
+ \sa setOffset(), sectionPosition()
+*/
+void QHeaderView::setOffsetToSectionPosition(int visualIndex)
+{
+ Q_D(QHeaderView);
+ if (visualIndex > -1 && visualIndex < d->sectionCount) {
+ int position = d->headerSectionPosition(d->adjustedVisualIndex(visualIndex));
+ setOffset(position);
+ }
+}
+
+/*!
+ \since 4.2
+ Sets the offset to make the last section visible.
+
+ \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
+*/
+void QHeaderView::setOffsetToLastSection()
+{
+ Q_D(const QHeaderView);
+ int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
+ int position = length() - size;
+ setOffset(position);
+}
+
+/*!
+ Returns the length along the orientation of the header.
+
+ \sa sizeHint(), setResizeMode(), offset()
+*/
+
+int QHeaderView::length() const
+{
+ Q_D(const QHeaderView);
+ //Q_ASSERT(d->headerLength() == d->length);
+ return d->length;
+}
+
+/*!
+ Returns a suitable size hint for this header.
+
+ \sa sectionSizeHint()
+*/
+
+QSize QHeaderView::sizeHint() const
+{
+ Q_D(const QHeaderView);
+ if (count() < 1)
+ return QSize(0, 0);
+ if (d->cachedSizeHint.isValid())
+ return d->cachedSizeHint;
+ int width = 0;
+ int height = 0;
+ // get size hint for the first n sections
+ int c = qMin(count(), 100);
+ for (int i = 0; i < c; ++i) {
+ if (isSectionHidden(i))
+ continue;
+ QSize hint = sectionSizeFromContents(i);
+ width = qMax(hint.width(), width);
+ height = qMax(hint.height(), height);
+ }
+ // get size hint for the last n sections
+ c = qMax(count() - 100, c);
+ for (int j = count() - 1; j >= c; --j) {
+ if (isSectionHidden(j))
+ continue;
+ QSize hint = sectionSizeFromContents(j);
+ width = qMax(hint.width(), width);
+ height = qMax(hint.height(), height);
+ }
+ d->cachedSizeHint = QSize(width, height);
+ return d->cachedSizeHint;
+}
+
+/*!
+ Returns a suitable size hint for the section specified by \a logicalIndex.
+
+ \sa sizeHint(), defaultSectionSize(), minimumSectionSize(),
+ Qt::SizeHintRole
+*/
+
+int QHeaderView::sectionSizeHint(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (isSectionHidden(logicalIndex))
+ return 0;
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return -1;
+ QSize size;
+ QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
+ if (value.isValid())
+ size = qvariant_cast<QSize>(value);
+ else
+ size = sectionSizeFromContents(logicalIndex);
+ int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
+ return qMax(minimumSectionSize(), hint);
+}
+
+/*!
+ Returns the visual index of the section that covers the given \a position
+ in the viewport.
+
+ \sa logicalIndexAt()
+*/
+
+int QHeaderView::visualIndexAt(int position) const
+{
+ Q_D(const QHeaderView);
+ int vposition = position;
+ d->executePostedLayout();
+ d->executePostedResize();
+ const int count = d->sectionCount;
+ if (count < 1)
+ return -1;
+
+ if (d->reverse())
+ vposition = d->viewport->width() - vposition;
+ vposition += d->offset;
+
+ if (vposition > d->length)
+ return -1;
+ int visual = d->headerVisualIndexAt(vposition);
+ if (visual < 0)
+ return -1;
+
+ while (d->isVisualIndexHidden(visual)){
+ ++visual;
+ if (visual >= count)
+ return -1;
+ }
+ return visual;
+}
+
+/*!
+ Returns the section that covers the given \a position in the viewport.
+
+ \sa visualIndexAt(), isSectionHidden()
+*/
+
+int QHeaderView::logicalIndexAt(int position) const
+{
+ const int visual = visualIndexAt(position);
+ if (visual > -1)
+ return logicalIndex(visual);
+ return -1;
+}
+
+/*!
+ Returns the width (or height for vertical headers) of the given
+ \a logicalIndex.
+
+ \sa length(), setResizeMode(), defaultSectionSize()
+*/
+
+int QHeaderView::sectionSize(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (isSectionHidden(logicalIndex))
+ return 0;
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return 0;
+ int visual = visualIndex(logicalIndex);
+ if (visual == -1)
+ return 0;
+ d->executePostedResize();
+ return d->headerSectionSize(visual);
+}
+
+/*!
+ Returns the section position of the given \a logicalIndex, or -1 if the
+ section is hidden.
+
+ \sa sectionViewportPosition()
+*/
+
+int QHeaderView::sectionPosition(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ // in some cases users may change the selections
+ // before we have a chance to do the layout
+ if (visual == -1)
+ return -1;
+ d->executePostedResize();
+ return d->headerSectionPosition(visual);
+}
+
+/*!
+ Returns the section viewport position of the given \a logicalIndex.
+
+ If the section is hidden, the return value is undefined.
+
+ \sa sectionPosition(), isSectionHidden()
+*/
+
+int QHeaderView::sectionViewportPosition(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (logicalIndex >= count())
+ return -1;
+ int position = sectionPosition(logicalIndex);
+ if (position < 0)
+ return position; // the section was hidden
+ int offsetPosition = position - d->offset;
+ if (d->reverse())
+ return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
+ return offsetPosition;
+}
+
+/*!
+ \fn int QHeaderView::logicalIndexAt(int x, int y) const
+
+ Returns the logical index of the section at the given coordinate. If the
+ header is horizontal \a x will be used, otherwise \a y will be used to
+ find the logical index.
+*/
+
+/*!
+ \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
+
+ Returns the logical index of the section at the position given in \a pos.
+ If the header is horizontal the x-coordinate will be used, otherwise the
+ y-coordinate will be used to find the logical index.
+
+ \sa sectionPosition()
+*/
+
+/*!
+ Moves the section at visual index \a from to occupy visual index \a to.
+
+ \sa sectionsMoved()
+*/
+
+void QHeaderView::moveSection(int from, int to)
+{
+ Q_D(QHeaderView);
+
+ d->executePostedLayout();
+ if (from < 0 || from >= d->sectionCount || to < 0 || to >= d->sectionCount)
+ return;
+
+ if (from == to) {
+ int logical = logicalIndex(from);
+ Q_ASSERT(logical != -1);
+ updateSection(logical);
+ return;
+ }
+
+ if (stretchLastSection() && to == d->lastVisibleVisualIndex())
+ d->lastSectionSize = sectionSize(from);
+
+ //int oldHeaderLength = length(); // ### for debugging; remove later
+ d->initializeIndexMapping();
+
+ QBitArray sectionHidden = d->sectionHidden;
+ int *visualIndices = d->visualIndices.data();
+ int *logicalIndices = d->logicalIndices.data();
+ int logical = logicalIndices[from];
+ int visual = from;
+
+ int affected_count = qAbs(to - from) + 1;
+ QVarLengthArray<int> sizes(affected_count);
+ QVarLengthArray<ResizeMode> modes(affected_count);
+
+ // move sections and indices
+ if (to > from) {
+ sizes[to - from] = d->headerSectionSize(from);
+ modes[to - from] = d->headerSectionResizeMode(from);
+ while (visual < to) {
+ sizes[visual - from] = d->headerSectionSize(visual + 1);
+ modes[visual - from] = d->headerSectionResizeMode(visual + 1);
+ if (!sectionHidden.isEmpty())
+ sectionHidden.setBit(visual, sectionHidden.testBit(visual + 1));
+ visualIndices[logicalIndices[visual + 1]] = visual;
+ logicalIndices[visual] = logicalIndices[visual + 1];
+ ++visual;
+ }
+ } else {
+ sizes[0] = d->headerSectionSize(from);
+ modes[0] = d->headerSectionResizeMode(from);
+ while (visual > to) {
+ sizes[visual - to] = d->headerSectionSize(visual - 1);
+ modes[visual - to] = d->headerSectionResizeMode(visual - 1);
+ if (!sectionHidden.isEmpty())
+ sectionHidden.setBit(visual, sectionHidden.testBit(visual - 1));
+ visualIndices[logicalIndices[visual - 1]] = visual;
+ logicalIndices[visual] = logicalIndices[visual - 1];
+ --visual;
+ }
+ }
+ if (!sectionHidden.isEmpty()) {
+ sectionHidden.setBit(to, d->sectionHidden.testBit(from));
+ d->sectionHidden = sectionHidden;
+ }
+ visualIndices[logical] = to;
+ logicalIndices[to] = logical;
+
+ //Q_ASSERT(oldHeaderLength == length());
+ // move sizes
+ // ### check for spans of section sizes here
+ if (to > from) {
+ for (visual = from; visual <= to; ++visual) {
+ int size = sizes[visual - from];
+ ResizeMode mode = modes[visual - from];
+ d->createSectionSpan(visual, visual, size, mode);
+ }
+ } else {
+ for (visual = to; visual <= from; ++visual) {
+ int size = sizes[visual - to];
+ ResizeMode mode = modes[visual - to];
+ d->createSectionSpan(visual, visual, size, mode);
+ }
+ }
+ //Q_ASSERT(d->headerLength() == length());
+ //Q_ASSERT(oldHeaderLength == length());
+ //Q_ASSERT(d->logicalIndices.count() == d->sectionCount);
+
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections();
+ d->viewport->update();
+
+ emit sectionMoved(logical, from, to);
+}
+
+/*!
+ \since 4.2
+ Swaps the section at visual index \a first with the section at visual
+ index \a second.
+
+ \sa moveSection()
+*/
+void QHeaderView::swapSections(int first, int second)
+{
+ Q_D(QHeaderView);
+
+ if (first == second)
+ return;
+ d->executePostedLayout();
+ if (first < 0 || first >= d->sectionCount || second < 0 || second >= d->sectionCount)
+ return;
+
+ int firstSize = d->headerSectionSize(first);
+ ResizeMode firstMode = d->headerSectionResizeMode(first);
+ int firstLogical = d->logicalIndex(first);
+
+ int secondSize = d->headerSectionSize(second);
+ ResizeMode secondMode = d->headerSectionResizeMode(second);
+ int secondLogical = d->logicalIndex(second);
+
+ d->createSectionSpan(second, second, firstSize, firstMode);
+ d->createSectionSpan(first, first, secondSize, secondMode);
+
+ d->initializeIndexMapping();
+
+ d->visualIndices[firstLogical] = second;
+ d->logicalIndices[second] = firstLogical;
+
+ d->visualIndices[secondLogical] = first;
+ d->logicalIndices[first] = secondLogical;
+
+ if (!d->sectionHidden.isEmpty()) {
+ bool firstHidden = d->sectionHidden.testBit(first);
+ bool secondHidden = d->sectionHidden.testBit(second);
+ d->sectionHidden.setBit(first, secondHidden);
+ d->sectionHidden.setBit(second, firstHidden);
+ }
+
+ d->viewport->update();
+ emit sectionMoved(firstLogical, first, second);
+ emit sectionMoved(secondLogical, second, first);
+}
+
+/*!
+ \fn void QHeaderView::resizeSection(int logicalIndex, int size)
+
+ Resizes the section specified by \a logicalIndex to \a size measured in
+ pixels.
+
+ \sa sectionResized(), resizeMode(), sectionSize()
+*/
+
+void QHeaderView::resizeSection(int logical, int size)
+{
+ Q_D(QHeaderView);
+ if (logical < 0 || logical >= count())
+ return;
+
+ if (isSectionHidden(logical)) {
+ d->hiddenSectionSize.insert(logical, size);
+ return;
+ }
+
+ int visual = visualIndex(logical);
+ if (visual == -1)
+ return;
+
+ int oldSize = d->headerSectionSize(visual);
+ if (oldSize == size)
+ return;
+
+ d->executePostedLayout();
+ d->invalidateCachedSizeHint();
+
+ if (stretchLastSection() && visual == d->lastVisibleVisualIndex())
+ d->lastSectionSize = size;
+
+ if (size != oldSize)
+ d->createSectionSpan(visual, visual, size, d->headerSectionResizeMode(visual));
+
+ int w = d->viewport->width();
+ int h = d->viewport->height();
+ int pos = sectionViewportPosition(logical);
+ QRect r;
+ if (d->orientation == Qt::Horizontal)
+ if (isRightToLeft())
+ r.setRect(0, 0, pos + size, h);
+ else
+ r.setRect(pos, 0, w - pos, h);
+ else
+ r.setRect(0, pos, w, h - pos);
+
+ if (d->hasAutoResizeSections()) {
+ d->doDelayedResizeSections();
+ r = d->viewport->rect();
+ }
+ d->viewport->update(r.normalized());
+ emit sectionResized(logical, oldSize, size);
+}
+
+/*!
+ Resizes the sections according to the given \a mode, ignoring the current
+ resize mode.
+
+ \sa resizeMode(), sectionResized()
+*/
+
+void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ d->resizeSections(mode, true);
+}
+
+/*!
+ \fn void QHeaderView::hideSection(int logicalIndex)
+ Hides the section specified by \a logicalIndex.
+
+ \sa showSection(), isSectionHidden(), hiddenSectionCount(),
+ setSectionHidden()
+*/
+
+/*!
+ \fn void QHeaderView::showSection(int logicalIndex)
+ Shows the section specified by \a logicalIndex.
+
+ \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
+ setSectionHidden()
+*/
+
+/*!
+ Returns true if the section specified by \a logicalIndex is explicitly
+ hidden from the user; otherwise returns false.
+
+ \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
+*/
+
+bool QHeaderView::isSectionHidden(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ d->executePostedLayout();
+ if (logicalIndex >= d->sectionHidden.count() || logicalIndex < 0 || logicalIndex >= d->sectionCount)
+ return false;
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ return d->sectionHidden.testBit(visual);
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of sections in the header that has been hidden.
+
+ \sa setSectionHidden(), isSectionHidden()
+*/
+int QHeaderView::hiddenSectionCount() const
+{
+ Q_D(const QHeaderView);
+ return d->hiddenSectionSize.count();
+}
+
+/*!
+ If \a hide is true the section specified by \a logicalIndex is hidden;
+ otherwise the section is shown.
+
+ \sa isSectionHidden(), hiddenSectionCount()
+*/
+
+void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
+{
+ Q_D(QHeaderView);
+ if (logicalIndex < 0 || logicalIndex >= count())
+ return;
+
+ d->executePostedLayout();
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ if (hide == d->isVisualIndexHidden(visual))
+ return;
+ if (hide) {
+ int size = d->headerSectionSize(visual);
+ if (!d->hasAutoResizeSections())
+ resizeSection(logicalIndex, 0);
+ d->hiddenSectionSize.insert(logicalIndex, size);
+ if (d->sectionHidden.count() < count())
+ d->sectionHidden.resize(count());
+ d->sectionHidden.setBit(visual, true);
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections();
+ } else {
+ int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
+ d->hiddenSectionSize.remove(logicalIndex);
+ if (d->hiddenSectionSize.isEmpty()) {
+ d->sectionHidden.clear();
+ } else {
+ Q_ASSERT(visual <= d->sectionHidden.count());
+ d->sectionHidden.setBit(visual, false);
+ }
+ resizeSection(logicalIndex, size);
+ }
+}
+
+/*!
+ Returns the number of sections in the header.
+
+ \sa sectionCountChanged(), length()
+*/
+
+int QHeaderView::count() const
+{
+ Q_D(const QHeaderView);
+ //Q_ASSERT(d->sectionCount == d->headerSectionCount());
+ // ### this may affect the lazy layout
+ d->executePostedLayout();
+ return d->sectionCount;
+}
+
+/*!
+ Returns the visual index position of the section specified by the given
+ \a logicalIndex, or -1 otherwise.
+
+ Hidden sections still have valid visual indexes.
+
+ \sa logicalIndex()
+*/
+
+int QHeaderView::visualIndex(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (logicalIndex < 0)
+ return -1;
+ d->executePostedLayout();
+ if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
+ if (logicalIndex < d->sectionCount)
+ return logicalIndex;
+ } else if (logicalIndex < d->visualIndices.count()) {
+ int visual = d->visualIndices.at(logicalIndex);
+ Q_ASSERT(visual < d->sectionCount);
+ return visual;
+ }
+ return -1;
+}
+
+/*!
+ Returns the logicalIndex for the section at the given \a visualIndex
+ position, or -1 otherwise.
+
+ \sa visualIndex(), sectionPosition()
+*/
+
+int QHeaderView::logicalIndex(int visualIndex) const
+{
+ Q_D(const QHeaderView);
+ if (visualIndex < 0 || visualIndex >= d->sectionCount)
+ return -1;
+ return d->logicalIndex(visualIndex);
+}
+
+/*!
+ If \a movable is true, the header may be moved by the user; otherwise it
+ is fixed in place.
+
+ \sa isMovable(), sectionMoved()
+*/
+
+// ### Qt 5: change to setSectionsMovable()
+void QHeaderView::setMovable(bool movable)
+{
+ Q_D(QHeaderView);
+ d->movableSections = movable;
+}
+
+/*!
+ Returns true if the header can be moved by the user; otherwise returns
+ false.
+
+ \sa setMovable()
+*/
+
+// ### Qt 5: change to sectionsMovable()
+bool QHeaderView::isMovable() const
+{
+ Q_D(const QHeaderView);
+ return d->movableSections;
+}
+
+/*!
+ If \a clickable is true, the header will respond to single clicks.
+
+ \sa isClickable(), sectionClicked(), sectionPressed(),
+ setSortIndicatorShown()
+*/
+
+// ### Qt 5: change to setSectionsClickable()
+void QHeaderView::setClickable(bool clickable)
+{
+ Q_D(QHeaderView);
+ d->clickableSections = clickable;
+}
+
+/*!
+ Returns true if the header is clickable; otherwise returns false. A
+ clickable header could be set up to allow the user to change the
+ representation of the data in the view related to the header.
+
+ \sa setClickable()
+*/
+
+// ### Qt 5: change to sectionsClickable()
+bool QHeaderView::isClickable() const
+{
+ Q_D(const QHeaderView);
+ return d->clickableSections;
+}
+
+void QHeaderView::setHighlightSections(bool highlight)
+{
+ Q_D(QHeaderView);
+ d->highlightSelected = highlight;
+}
+
+bool QHeaderView::highlightSections() const
+{
+ Q_D(const QHeaderView);
+ return d->highlightSelected;
+}
+
+/*!
+ Sets the constraints on how the header can be resized to those described
+ by the given \a mode.
+
+ \sa resizeMode(), length(), sectionResized(), sectionAutoResize()
+*/
+
+void QHeaderView::setResizeMode(ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ initializeSections();
+ d->stretchSections = (mode == Stretch ? count() : 0);
+ d->contentsSections = (mode == ResizeToContents ? count() : 0);
+ d->setGlobalHeaderResizeMode(mode);
+ if (d->hasAutoResizeSections())
+ d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
+}
+
+/*!
+ \overload
+
+ Sets the constraints on how the section specified by \a logicalIndex in
+ the header can be resized to those described by the given \a mode.
+
+ \note This setting will be ignored for the last section if the stretchLastSection
+ property is set to true. This is the default for the horizontal headers provided
+ by QTreeView.
+
+ \sa setStretchLastSection()
+*/
+
+// ### Qt 5: change to setSectionResizeMode()
+void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
+{
+ Q_D(QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+
+ ResizeMode old = d->headerSectionResizeMode(visual);
+ d->setHeaderSectionResizeMode(visual, mode);
+
+ if (mode == Stretch && old != Stretch)
+ ++d->stretchSections;
+ else if (mode == ResizeToContents && old != ResizeToContents)
+ ++d->contentsSections;
+ else if (mode != Stretch && old == Stretch)
+ --d->stretchSections;
+ else if (mode != ResizeToContents && old == ResizeToContents)
+ --d->contentsSections;
+
+ if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
+ d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
+}
+
+/*!
+ Returns the resize mode that applies to the section specified by the given
+ \a logicalIndex.
+
+ \sa setResizeMode()
+*/
+
+QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ return d->visualIndexResizeMode(visual);
+}
+
+/*!
+ \since 4.1
+
+ Returns the number of sections that are set to resize mode stretch. In
+ views, this can be used to see if the headerview needs to resize the
+ sections when the view's geometry changes.
+
+ \sa stretchLastSection, resizeMode()
+*/
+
+int QHeaderView::stretchSectionCount() const
+{
+ Q_D(const QHeaderView);
+ return d->stretchSections;
+}
+
+/*!
+ \property QHeaderView::showSortIndicator
+ \brief whether the sort indicator is shown
+
+ By default, this property is false.
+
+ \sa setClickable()
+*/
+
+void QHeaderView::setSortIndicatorShown(bool show)
+{
+ Q_D(QHeaderView);
+ if (d->sortIndicatorShown == show)
+ return;
+
+ d->sortIndicatorShown = show;
+
+ if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
+ return;
+
+ if (d->visualIndexResizeMode(sortIndicatorSection()) == ResizeToContents)
+ resizeSections();
+
+ d->viewport->update();
+}
+
+bool QHeaderView::isSortIndicatorShown() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorShown;
+}
+
+/*!
+ Sets the sort indicator for the section specified by the given
+ \a logicalIndex in the direction specified by \a order, and removes the
+ sort indicator from any other section that was showing it.
+
+ \a logicalIndex may be -1, in which case no sort indicator will be shown
+ and the model will return to its natural, unsorted order. Note that not
+ all models support this and may even crash in this case.
+
+ \sa sortIndicatorSection() sortIndicatorOrder()
+*/
+
+void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
+{
+ Q_D(QHeaderView);
+
+ // This is so that people can set the position of the sort indicator before the fill the model
+ int old = d->sortIndicatorSection;
+ d->sortIndicatorSection = logicalIndex;
+ d->sortIndicatorOrder = order;
+
+ if (logicalIndex >= d->sectionCount)
+ return; // nothing to do
+
+ if (old != logicalIndex
+ && ((logicalIndex >= 0 && resizeMode(logicalIndex) == ResizeToContents)
+ || old >= d->sectionCount || (old >= 0 && resizeMode(old) == ResizeToContents))) {
+ resizeSections();
+ d->viewport->update();
+ } else {
+ if (old >= 0 && old != logicalIndex)
+ updateSection(old);
+ if (logicalIndex >= 0)
+ updateSection(logicalIndex);
+ }
+
+ emit sortIndicatorChanged(logicalIndex, order);
+}
+
+/*!
+ Returns the logical index of the section that has a sort indicator.
+ By default this is section 0.
+
+ \sa setSortIndicator() sortIndicatorOrder() setSortIndicatorShown()
+*/
+
+int QHeaderView::sortIndicatorSection() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorSection;
+}
+
+/*!
+ Returns the order for the sort indicator. If no section has a sort
+ indicator the return value of this function is undefined.
+
+ \sa setSortIndicator() sortIndicatorSection()
+*/
+
+Qt::SortOrder QHeaderView::sortIndicatorOrder() const
+{
+ Q_D(const QHeaderView);
+ return d->sortIndicatorOrder;
+}
+
+/*!
+ \property QHeaderView::stretchLastSection
+ \brief whether the last visible section in the header takes up all the
+ available space
+
+ The default value is false.
+
+ \note The horizontal headers provided by QTreeView are configured with this
+ property set to true, ensuring that the view does not waste any of the
+ space assigned to it for its header. If this value is set to true, this
+ property will override the resize mode set on the last section in the
+ header.
+
+ \sa setResizeMode()
+*/
+bool QHeaderView::stretchLastSection() const
+{
+ Q_D(const QHeaderView);
+ return d->stretchLastSection;
+}
+
+void QHeaderView::setStretchLastSection(bool stretch)
+{
+ Q_D(QHeaderView);
+ d->stretchLastSection = stretch;
+ if (d->state != QHeaderViewPrivate::NoState)
+ return;
+ if (stretch)
+ resizeSections();
+ else if (count())
+ resizeSection(count() - 1, d->defaultSectionSize);
+}
+
+/*!
+ \since 4.2
+ \property QHeaderView::cascadingSectionResizes
+ \brief whether interactive resizing will be cascaded to the following
+ sections once the section being resized by the user has reached its
+ minimum size
+
+ This property only affects sections that have \l Interactive as their
+ resize mode.
+
+ The default value is false.
+
+ \sa setResizeMode()
+*/
+bool QHeaderView::cascadingSectionResizes() const
+{
+ Q_D(const QHeaderView);
+ return d->cascadingResizing;
+}
+
+void QHeaderView::setCascadingSectionResizes(bool enable)
+{
+ Q_D(QHeaderView);
+ d->cascadingResizing = enable;
+}
+
+/*!
+ \property QHeaderView::defaultSectionSize
+ \brief the default size of the header sections before resizing.
+
+ This property only affects sections that have \l Interactive or \l Fixed
+ as their resize mode.
+
+ \sa setResizeMode() minimumSectionSize
+*/
+int QHeaderView::defaultSectionSize() const
+{
+ Q_D(const QHeaderView);
+ return d->defaultSectionSize;
+}
+
+void QHeaderView::setDefaultSectionSize(int size)
+{
+ Q_D(QHeaderView);
+ d->defaultSectionSize = size;
+ d->forceInitializing = true;
+}
+
+/*!
+ \since 4.2
+ \property QHeaderView::minimumSectionSize
+ \brief the minimum size of the header sections.
+
+ The minimum section size is the smallest section size allowed. If the
+ minimum section size is set to -1, QHeaderView will use the maximum of
+ the \l{QApplication::globalStrut()}{global strut} or the
+ \l{fontMetrics()}{font metrics} size.
+
+ This property is honored by all \l{ResizeMode}{resize modes}.
+
+ \sa setResizeMode() defaultSectionSize
+*/
+int QHeaderView::minimumSectionSize() const
+{
+ Q_D(const QHeaderView);
+ if (d->minimumSectionSize == -1) {
+ QSize strut = QApplication::globalStrut();
+ int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
+ if (d->orientation == Qt::Horizontal)
+ return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
+ return qMax(strut.height(), (fontMetrics().lineSpacing() + margin));
+ }
+ return d->minimumSectionSize;
+}
+
+void QHeaderView::setMinimumSectionSize(int size)
+{
+ Q_D(QHeaderView);
+ d->minimumSectionSize = size;
+}
+
+/*!
+ \since 4.1
+ \property QHeaderView::defaultAlignment
+ \brief the default alignment of the text in each header section
+*/
+
+Qt::Alignment QHeaderView::defaultAlignment() const
+{
+ Q_D(const QHeaderView);
+ return d->defaultAlignment;
+}
+
+void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
+{
+ Q_D(QHeaderView);
+ if (d->defaultAlignment == alignment)
+ return;
+
+ d->defaultAlignment = alignment;
+ d->viewport->update();
+}
+
+/*!
+ \internal
+*/
+void QHeaderView::doItemsLayout()
+{
+ initializeSections();
+ QAbstractItemView::doItemsLayout();
+}
+
+/*!
+ Returns true if sections in the header has been moved; otherwise returns
+ false;
+
+ \sa moveSection()
+*/
+bool QHeaderView::sectionsMoved() const
+{
+ Q_D(const QHeaderView);
+ return !d->visualIndices.isEmpty();
+}
+
+/*!
+ \since 4.1
+
+ Returns true if sections in the header has been hidden; otherwise returns
+ false;
+
+ \sa setSectionHidden()
+*/
+bool QHeaderView::sectionsHidden() const
+{
+ Q_D(const QHeaderView);
+ return !d->hiddenSectionSize.isEmpty();
+}
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \since 4.3
+
+ Saves the current state of this header view.
+
+ To restore the saved state, pass the return value to restoreState().
+
+ \sa restoreState()
+*/
+QByteArray QHeaderView::saveState() const
+{
+ Q_D(const QHeaderView);
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << QHeaderViewPrivate::VersionMarker;
+ stream << 0; // current version is 0
+ d->write(stream);
+ return data;
+}
+
+/*!
+ \since 4.3
+ Restores the \a state of this header view.
+ This function returns \c true if the state was restored; otherwise returns
+ false.
+
+ \sa saveState()
+*/
+bool QHeaderView::restoreState(const QByteArray &state)
+{
+ Q_D(QHeaderView);
+ if (state.isEmpty())
+ return false;
+ QByteArray data = state;
+ QDataStream stream(&data, QIODevice::ReadOnly);
+ int marker;
+ int ver;
+ stream >> marker;
+ stream >> ver;
+ if (stream.status() != QDataStream::Ok
+ || marker != QHeaderViewPrivate::VersionMarker
+ || ver != 0) // current version is 0
+ return false;
+
+ if (d->read(stream)) {
+ emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
+ d->viewport->update();
+ return true;
+ }
+ return false;
+}
+#endif
+
+/*!
+ \reimp
+*/
+void QHeaderView::reset()
+{
+ QAbstractItemView::reset();
+ // it would be correct to call clear, but some apps rely
+ // on the header keeping the sections, even after calling reset
+ //d->clear();
+ initializeSections();
+}
+
+/*!
+ Updates the changed header sections with the given \a orientation, from
+ \a logicalFirst to \a logicalLast inclusive.
+*/
+void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
+{
+ Q_D(QHeaderView);
+ if (d->orientation != orientation)
+ return;
+
+ if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
+ return;
+
+ d->invalidateCachedSizeHint();
+
+ int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
+
+ for (int section = logicalFirst; section <= logicalLast; ++section) {
+ const int visual = visualIndex(section);
+ firstVisualIndex = qMin(firstVisualIndex, visual);
+ lastVisualIndex = qMax(lastVisualIndex, visual);
+ }
+
+ d->executePostedResize();
+ const int first = d->headerSectionPosition(firstVisualIndex),
+ last = d->headerSectionPosition(lastVisualIndex)
+ + d->headerSectionSize(lastVisualIndex);
+
+ if (orientation == Qt::Horizontal) {
+ d->viewport->update(first, 0, last - first, d->viewport->height());
+ } else {
+ d->viewport->update(0, first, d->viewport->width(), last - first);
+ }
+}
+
+/*!
+ \internal
+ \since 4.2
+
+ Updates the section specified by the given \a logicalIndex.
+*/
+
+void QHeaderView::updateSection(int logicalIndex)
+{
+ Q_D(QHeaderView);
+ if (d->orientation == Qt::Horizontal)
+ d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
+ 0, sectionSize(logicalIndex), d->viewport->height()));
+ else
+ d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
+ d->viewport->width(), sectionSize(logicalIndex)));
+}
+
+/*!
+ Resizes the sections according to their size hints. Normally, you do not
+ have to call this function.
+*/
+
+void QHeaderView::resizeSections()
+{
+ Q_D(QHeaderView);
+ if (d->hasAutoResizeSections())
+ d->resizeSections(Interactive, false); // no global resize mode
+}
+
+/*!
+ This slot is called when sections are inserted into the \a parent.
+ \a logicalFirst and \a logicalLast indices signify where the new sections
+ were inserted.
+
+ If only one section is inserted, \a logicalFirst and \a logicalLast will
+ be the same.
+*/
+
+void QHeaderView::sectionsInserted(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_D(QHeaderView);
+ if (parent != d->root)
+ return; // we only handle changes in the top level
+ int oldCount = d->sectionCount;
+
+ d->invalidateCachedSizeHint();
+
+ // add the new sections
+ int insertAt = 0;
+ for (int spanStart = 0; insertAt < d->sectionSpans.count() && spanStart < logicalFirst; ++insertAt)
+ spanStart += d->sectionSpans.at(insertAt).count;
+
+ int insertCount = logicalLast - logicalFirst + 1;
+ d->sectionCount += insertCount;
+
+ if (d->sectionSpans.isEmpty() || insertAt >= d->sectionSpans.count()) {
+ int insertLength = d->defaultSectionSize * insertCount;
+ d->length += insertLength;
+ QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
+ d->sectionSpans.append(span);
+ } else if ((d->sectionSpans.at(insertAt).sectionSize() == d->defaultSectionSize)
+ && d->sectionSpans.at(insertAt).resizeMode == d->globalResizeMode) {
+ // add the new sections to an existing span
+ int insertLength = d->sectionSpans.at(insertAt).sectionSize() * insertCount;
+ d->length += insertLength;
+ d->sectionSpans[insertAt].size += insertLength;
+ d->sectionSpans[insertAt].count += insertCount;
+ } else {
+ // separate them out into their own span
+ int insertLength = d->defaultSectionSize * insertCount;
+ d->length += insertLength;
+ QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
+ d->sectionSpans.insert(insertAt, span);
+ }
+
+ // update sorting column
+ if (d->sortIndicatorSection >= logicalFirst)
+ d->sortIndicatorSection += insertCount;
+
+ // update resize mode section counts
+ if (d->globalResizeMode == Stretch)
+ d->stretchSections = d->sectionCount;
+ else if (d->globalResizeMode == ResizeToContents)
+ d->contentsSections = d->sectionCount;
+
+ // clear selection cache
+ d->sectionSelected.clear();
+
+ // update mapping
+ if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
+ Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
+ int mappingCount = d->visualIndices.count();
+ for (int i = 0; i < mappingCount; ++i) {
+ if (d->visualIndices.at(i) >= logicalFirst)
+ d->visualIndices[i] += insertCount;
+ if (d->logicalIndices.at(i) >= logicalFirst)
+ d->logicalIndices[i] += insertCount;
+ }
+ for (int j = logicalFirst; j <= logicalLast; ++j) {
+ d->visualIndices.insert(j, j);
+ d->logicalIndices.insert(j, j);
+ }
+ }
+
+ // insert sections into sectionsHidden
+ if (!d->sectionHidden.isEmpty()) {
+ QBitArray sectionHidden(d->sectionHidden);
+ sectionHidden.resize(sectionHidden.count() + insertCount);
+ //sectionHidden.fill(false, logicalFirst, logicalLast + 1);
+ for (int i = logicalFirst; i <= logicalLast; ++i)
+ // visual == logical in this range (see previous block)
+ sectionHidden.setBit(i, false);
+ for (int j = logicalLast + 1; j < sectionHidden.count(); ++j)
+ sectionHidden.setBit(d->visualIndex(j),
+ d->sectionHidden.testBit(d->visualIndex(j - insertCount)));
+ d->sectionHidden = sectionHidden;
+ }
+
+ // insert sections into hiddenSectionSize
+ QHash<int, int> newHiddenSectionSize; // from logical index to section size
+ for (int i = 0; i < logicalFirst; ++i)
+ if (isSectionHidden(i))
+ newHiddenSectionSize[i] = d->hiddenSectionSize[i];
+ for (int j = logicalLast + 1; j < d->sectionCount; ++j)
+ if (isSectionHidden(j))
+ newHiddenSectionSize[j] = d->hiddenSectionSize[j - insertCount];
+ d->hiddenSectionSize = newHiddenSectionSize;
+
+ d->doDelayedResizeSections();
+ emit sectionCountChanged(oldCount, count());
+
+ // if the new sections were not updated by resizing, we need to update now
+ if (!d->hasAutoResizeSections())
+ d->viewport->update();
+}
+
+/*!
+ This slot is called when sections are removed from the \a parent.
+ \a logicalFirst and \a logicalLast signify where the sections were removed.
+
+ If only one section is removed, \a logicalFirst and \a logicalLast will
+ be the same.
+*/
+
+void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_UNUSED(parent);
+ Q_UNUSED(logicalFirst);
+ Q_UNUSED(logicalLast);
+}
+
+void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
+{
+ Q_Q(QHeaderView);
+ const int changeCount = logicalLast - logicalFirst + 1;
+
+ // remove sections from hiddenSectionSize
+ QHash<int, int> newHiddenSectionSize; // from logical index to section size
+ for (int i = 0; i < logicalFirst; ++i)
+ if (q->isSectionHidden(i))
+ newHiddenSectionSize[i] = hiddenSectionSize[i];
+ for (int j = logicalLast + 1; j < sectionCount; ++j)
+ if (q->isSectionHidden(j))
+ newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
+ hiddenSectionSize = newHiddenSectionSize;
+
+ // remove sections from sectionsHidden
+ if (!sectionHidden.isEmpty()) {
+ const int newsize = qMin(sectionCount - changeCount, sectionHidden.size());
+ QBitArray newSectionHidden(newsize);
+ for (int j = 0, k = 0; j < sectionHidden.size(); ++j) {
+ const int logical = logicalIndex(j);
+ if (logical < logicalFirst || logical > logicalLast) {
+ newSectionHidden[k++] = sectionHidden[j];
+ }
+ }
+ sectionHidden = newSectionHidden;
+ }
+}
+
+void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
+ int logicalFirst, int logicalLast)
+{
+ Q_Q(QHeaderView);
+ if (parent != root)
+ return; // we only handle changes in the top level
+ if (qMin(logicalFirst, logicalLast) < 0
+ || qMax(logicalLast, logicalFirst) >= sectionCount)
+ return;
+ int oldCount = q->count();
+ int changeCount = logicalLast - logicalFirst + 1;
+
+ updateHiddenSections(logicalFirst, logicalLast);
+
+ if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
+ //Q_ASSERT(headerSectionCount() == sectionCount);
+ removeSectionsFromSpans(logicalFirst, logicalLast);
+ } else {
+ for (int l = logicalLast; l >= logicalFirst; --l) {
+ int visual = visualIndices.at(l);
+ for (int v = 0; v < sectionCount; ++v) {
+ if (v >= logicalIndices.count())
+ continue; // the section doesn't exist
+ if (v > visual) {
+ int logical = logicalIndices.at(v);
+ --(visualIndices[logical]);
+ }
+ if (logicalIndex(v) > l) // no need to move the positions before l
+ --(logicalIndices[v]);
+ }
+ logicalIndices.remove(visual);
+ visualIndices.remove(l);
+ //Q_ASSERT(headerSectionCount() == sectionCount);
+ removeSectionsFromSpans(visual, visual);
+ }
+ // ### handle sectionSelection, sectionHidden
+ }
+ sectionCount -= changeCount;
+
+ // update sorting column
+ if (sortIndicatorSection >= logicalFirst) {
+ if (sortIndicatorSection <= logicalLast)
+ sortIndicatorSection = -1;
+ else
+ sortIndicatorSection -= changeCount;
+ }
+
+ // if we only have the last section (the "end" position) left, the header is empty
+ if (sectionCount <= 0)
+ clear();
+ invalidateCachedSizeHint();
+ emit q->sectionCountChanged(oldCount, q->count());
+ viewport->update();
+}
+
+void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
+{
+ //if there is no row/column we can't have mapping for columns
+ //because no QModelIndex in the model would be valid
+ // ### this is far from being bullet-proof and we would need a real system to
+ // ### map columns or rows persistently
+ if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
+ || model->columnCount(root) == 0)
+ return;
+
+ for (int i = 0; i < sectionHidden.count(); ++i)
+ if (sectionHidden.testBit(i)) // ### note that we are using column or row 0
+ persistentHiddenSections.append(orientation == Qt::Horizontal
+ ? model->index(0, logicalIndex(i), root)
+ : model->index(logicalIndex(i), 0, root));
+}
+
+void QHeaderViewPrivate::_q_layoutChanged()
+{
+ Q_Q(QHeaderView);
+ viewport->update();
+ if (persistentHiddenSections.isEmpty() || modelIsEmpty()) {
+ if (modelSectionCount() != sectionCount)
+ q->initializeSections();
+ persistentHiddenSections.clear();
+ return;
+ }
+ bool sectionCountChanged = false;
+ for (int i = 0; i < sectionHidden.count(); ++i) {
+ if (sectionHidden.testBit(i))
+ q->setSectionHidden(logicalIndex(i), false);
+ }
+
+ for (int i = 0; i < persistentHiddenSections.count(); ++i) {
+ QModelIndex index = persistentHiddenSections.at(i);
+ if (index.isValid()) {
+ const int logical = (orientation == Qt::Horizontal
+ ? index.column()
+ : index.row());
+ q->setSectionHidden(logical, true);
+ } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) {
+ sectionCountChanged = true;
+ break;
+ }
+ }
+ persistentHiddenSections.clear();
+
+ // the number of sections changed; we need to reread the state of the model
+ if (sectionCountChanged)
+ q->initializeSections();
+}
+
+/*!
+ \internal
+*/
+
+void QHeaderView::initializeSections()
+{
+ Q_D(QHeaderView);
+ const int oldCount = d->sectionCount;
+ const int newCount = d->modelSectionCount();
+ if (newCount <= 0) {
+ d->clear();
+ emit sectionCountChanged(oldCount, 0);
+ } else if (newCount != oldCount) {
+ const int min = qBound(0, oldCount, newCount - 1);
+ initializeSections(min, newCount - 1);
+ if (stretchLastSection()) // we've already gotten the size hint
+ d->lastSectionSize = sectionSize(logicalIndex(d->sectionCount - 1));
+
+ //make sure we update the hidden sections
+ if (newCount < oldCount)
+ d->updateHiddenSections(0, newCount-1);
+ } else if (d->forceInitializing) {
+ initializeSections(0, newCount - 1);
+ d->forceInitializing = false;
+ }
+}
+
+/*!
+ \internal
+*/
+
+void QHeaderView::initializeSections(int start, int end)
+{
+ Q_D(QHeaderView);
+
+ Q_ASSERT(start >= 0);
+ Q_ASSERT(end >= 0);
+
+ d->executePostedLayout();
+ d->invalidateCachedSizeHint();
+
+ if (end + 1 < d->sectionCount) {
+ int newCount = end + 1;
+ d->removeSectionsFromSpans(newCount, d->sectionCount);
+ if (!d->hiddenSectionSize.isEmpty()) {
+ if (d->sectionCount - newCount > d->hiddenSectionSize.count()) {
+ for (int i = end + 1; i < d->sectionCount; ++i)
+ d->hiddenSectionSize.remove(i);
+ } else {
+ QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
+ while (it != d->hiddenSectionSize.end()) {
+ if (it.key() > end)
+ it = d->hiddenSectionSize.erase(it);
+ else
+ ++it;
+ }
+ }
+ }
+ }
+
+ int oldCount = d->sectionCount;
+ d->sectionCount = end + 1;
+
+ if (!d->logicalIndices.isEmpty()) {
+ d->logicalIndices.resize(d->sectionCount);
+ d->visualIndices.resize(d->sectionCount);
+ for (int i = start; i < d->sectionCount; ++i){
+ d->logicalIndices[i] = i;
+ d->visualIndices[i] = i;
+ }
+ }
+
+ if (d->globalResizeMode == Stretch)
+ d->stretchSections = d->sectionCount;
+ else if (d->globalResizeMode == ResizeToContents)
+ d->contentsSections = d->sectionCount;
+ if (!d->sectionHidden.isEmpty())
+ d->sectionHidden.resize(d->sectionCount);
+
+ if (d->sectionCount > oldCount || d->forceInitializing)
+ d->createSectionSpan(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
+ //Q_ASSERT(d->headerLength() == d->length);
+
+ if (d->sectionCount != oldCount)
+ emit sectionCountChanged(oldCount, d->sectionCount);
+ d->viewport->update();
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
+{
+ Q_D(QHeaderView);
+
+ if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
+ if (old.isValid() && old.parent() == d->root)
+ d->setDirtyRegion(QRect(sectionViewportPosition(old.column()), 0,
+ sectionSize(old.column()), d->viewport->height()));
+ if (current.isValid() && current.parent() == d->root)
+ d->setDirtyRegion(QRect(sectionViewportPosition(current.column()), 0,
+ sectionSize(current.column()), d->viewport->height()));
+ } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
+ if (old.isValid() && old.parent() == d->root)
+ d->setDirtyRegion(QRect(0, sectionViewportPosition(old.row()),
+ d->viewport->width(), sectionSize(old.row())));
+ if (current.isValid() && current.parent() == d->root)
+ d->setDirtyRegion(QRect(0, sectionViewportPosition(current.row()),
+ d->viewport->width(), sectionSize(current.row())));
+ }
+ d->updateDirtyRegion();
+}
+
+
+/*!
+ \reimp
+*/
+
+bool QHeaderView::event(QEvent *e)
+{
+ Q_D(QHeaderView);
+ switch (e->type()) {
+ case QEvent::HoverEnter: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(e);
+ d->hover = logicalIndexAt(he->pos());
+ if (d->hover != -1)
+ updateSection(d->hover);
+ break; }
+ case QEvent::Leave:
+ case QEvent::HoverLeave: {
+ if (d->hover != -1)
+ updateSection(d->hover);
+ d->hover = -1;
+ break; }
+ case QEvent::HoverMove: {
+ QHoverEvent *he = static_cast<QHoverEvent*>(e);
+ int oldHover = d->hover;
+ d->hover = logicalIndexAt(he->pos());
+ if (d->hover != oldHover) {
+ if (oldHover != -1)
+ updateSection(oldHover);
+ if (d->hover != -1)
+ updateSection(d->hover);
+ }
+ break; }
+ case QEvent::Timer: { // ### reimplement timerEvent() instead ?
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ if (te->timerId() == d->delayedResize.timerId()) {
+ d->delayedResize.stop();
+ resizeSections();
+ }
+ break; }
+ default:
+ break;
+ }
+ return QAbstractItemView::event(e);
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::paintEvent(QPaintEvent *e)
+{
+ Q_D(QHeaderView);
+
+ if (count() == 0)
+ return;
+
+ QPainter painter(d->viewport);
+ const QPoint offset = d->scrollDelayOffset;
+ QRect translatedEventRect = e->rect();
+ translatedEventRect.translate(offset);
+
+ int start = -1;
+ int end = -1;
+ if (d->orientation == Qt::Horizontal) {
+ start = visualIndexAt(translatedEventRect.left());
+ end = visualIndexAt(translatedEventRect.right());
+ } else {
+ start = visualIndexAt(translatedEventRect.top());
+ end = visualIndexAt(translatedEventRect.bottom());
+ }
+
+ if (d->reverse()) {
+ start = (start == -1 ? count() - 1 : start);
+ end = (end == -1 ? 0 : end);
+ } else {
+ start = (start == -1 ? 0 : start);
+ end = (end == -1 ? count() - 1 : end);
+ }
+
+ int tmp = start;
+ start = qMin(start, end);
+ end = qMax(tmp, end);
+
+ d->prepareSectionSelected(); // clear and resize the bit array
+
+ QRect currentSectionRect;
+ int logical;
+ const int width = d->viewport->width();
+ const int height = d->viewport->height();
+ for (int i = start; i <= end; ++i) {
+ if (d->isVisualIndexHidden(i))
+ continue;
+ painter.save();
+ logical = logicalIndex(i);
+ if (d->orientation == Qt::Horizontal) {
+ currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height);
+ } else {
+ currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical));
+ }
+ currentSectionRect.translate(offset);
+
+ QVariant variant = d->model->headerData(logical, d->orientation,
+ Qt::FontRole);
+ if (variant.isValid() && qVariantCanConvert<QFont>(variant)) {
+ QFont sectionFont = qvariant_cast<QFont>(variant);
+ painter.setFont(sectionFont);
+ }
+ paintSection(&painter, currentSectionRect, logical);
+ painter.restore();
+ }
+
+ QStyleOption opt;
+ opt.init(this);
+ // Paint the area beyond where there are indexes
+ if (d->reverse()) {
+ opt.state |= QStyle::State_Horizontal;
+ if (currentSectionRect.left() > translatedEventRect.left()) {
+ opt.rect = QRect(translatedEventRect.left(), 0,
+ currentSectionRect.left() - translatedEventRect.left(), height);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ }
+ } else if (currentSectionRect.right() < translatedEventRect.right()) {
+ // paint to the right
+ opt.state |= QStyle::State_Horizontal;
+ opt.rect = QRect(currentSectionRect.right() + 1, 0,
+ translatedEventRect.right() - currentSectionRect.right(), height);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
+ // paint the bottom section
+ opt.state &= ~QStyle::State_Horizontal;
+ opt.rect = QRect(0, currentSectionRect.bottom() + 1,
+ width, height - currentSectionRect.bottom() - 1);
+ style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
+ }
+
+#if 0
+ // ### visualize section spans
+ for (int a = 0, i = 0; i < d->sectionSpans.count(); ++i) {
+ QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
+ if (d->orientation == Qt::Horizontal)
+ painter.fillRect(a - d->offset, 0, d->sectionSpans.at(i).size, 4, color);
+ else
+ painter.fillRect(0, a - d->offset, 4, d->sectionSpans.at(i).size, color);
+ a += d->sectionSpans.at(i).size;
+ }
+
+#endif
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mousePressEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
+ return;
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ int handle = d->sectionHandleAt(pos);
+ d->originalSize = -1; // clear the stored original size
+ if (handle == -1) {
+ d->pressed = logicalIndexAt(pos);
+ if (d->clickableSections)
+ emit sectionPressed(d->pressed);
+ if (d->movableSections) {
+ d->section = d->target = d->pressed;
+ if (d->section == -1)
+ return;
+ d->state = QHeaderViewPrivate::MoveSection;
+ d->setupSectionIndicator(d->section, pos);
+ } else if (d->clickableSections && d->pressed != -1) {
+ updateSection(d->pressed);
+ d->state = QHeaderViewPrivate::SelectSections;
+ }
+ } else if (resizeMode(handle) == Interactive) {
+ d->originalSize = sectionSize(handle);
+ d->state = QHeaderViewPrivate::ResizeSection;
+ d->section = handle;
+ }
+
+ d->firstPos = pos;
+ d->lastPos = pos;
+
+ d->clearCascadingSections();
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseMoveEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ if (pos < 0)
+ return;
+ if (e->buttons() == Qt::NoButton) {
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = -1;
+ }
+ switch (d->state) {
+ case QHeaderViewPrivate::ResizeSection: {
+ Q_ASSERT(d->originalSize != -1);
+ if (d->cascadingResizing) {
+ int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
+ int visual = visualIndex(d->section);
+ d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
+ } else {
+ int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
+ resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize()));
+ }
+ d->lastPos = pos;
+ return;
+ }
+ case QHeaderViewPrivate::MoveSection: {
+ if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()) {
+ int indicatorCenter = (d->orientation == Qt::Horizontal
+ ? d->sectionIndicator->width()
+ : d->sectionIndicator->height()) / 2;
+ int centerOffset = indicatorCenter - d->sectionIndicatorOffset;
+ // This will drop the moved section to the position under the center of the indicator.
+ // If centerOffset is 0, the section will be moved to the position of the mouse cursor.
+ int visual = visualIndexAt(pos + centerOffset);
+ if (visual == -1)
+ return;
+ d->target = d->logicalIndex(visual);
+ d->updateSectionIndicator(d->section, pos);
+ } else {
+ int visual = visualIndexAt(d->firstPos);
+ if (visual == -1)
+ return;
+ d->target = d->logicalIndex(visual);
+ d->updateSectionIndicator(d->section, d->firstPos);
+ }
+ return;
+ }
+ case QHeaderViewPrivate::SelectSections: {
+ int logical = logicalIndexAt(pos);
+ if (logical == d->pressed)
+ return; // nothing to do
+ else if (d->pressed != -1)
+ updateSection(d->pressed);
+ d->pressed = logical;
+ if (d->clickableSections && logical != -1) {
+ emit sectionEntered(d->pressed);
+ updateSection(d->pressed);
+ }
+ return;
+ }
+ case QHeaderViewPrivate::NoState: {
+#ifndef QT_NO_CURSOR
+ int handle = d->sectionHandleAt(pos);
+ bool hasCursor = testAttribute(Qt::WA_SetCursor);
+ if (handle != -1 && (resizeMode(handle) == Interactive)) {
+ if (!hasCursor)
+ setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
+ } else if (hasCursor) {
+ unsetCursor();
+ }
+#endif
+ return;
+ }
+ default:
+ break;
+ }
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ switch (d->state) {
+ case QHeaderViewPrivate::MoveSection:
+ if (!d->sectionIndicator->isHidden()) { // moving
+ int from = visualIndex(d->section);
+ Q_ASSERT(from != -1);
+ int to = visualIndex(d->target);
+ Q_ASSERT(to != -1);
+ moveSection(from, to);
+ d->section = d->target = -1;
+ d->updateSectionIndicator(d->section, pos);
+ break;
+ } // not moving
+ case QHeaderViewPrivate::SelectSections:
+ if (!d->clickableSections) {
+ int section = logicalIndexAt(pos);
+ updateSection(section);
+ }
+ // fall through
+ case QHeaderViewPrivate::NoState:
+ if (d->clickableSections) {
+ int section = logicalIndexAt(pos);
+ if (section != -1 && section == d->pressed) {
+ d->flipSortIndicator(section);
+ emit sectionClicked(logicalIndexAt(pos));
+ }
+ if (d->pressed != -1)
+ updateSection(d->pressed);
+ }
+ break;
+ case QHeaderViewPrivate::ResizeSection:
+ d->originalSize = -1;
+ d->clearCascadingSections();
+ break;
+ default:
+ break;
+ }
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = -1;
+}
+
+/*!
+ \reimp
+*/
+
+void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
+{
+ Q_D(QHeaderView);
+ int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
+ int handle = d->sectionHandleAt(pos);
+ if (handle > -1 && resizeMode(handle) == Interactive) {
+ emit sectionHandleDoubleClicked(handle);
+#ifndef QT_NO_CURSOR
+ Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
+ ? Qt::SplitHCursor : Qt::SplitVCursor;
+ if (cursor().shape() == splitCursor) {
+ // signal handlers may have changed the section size
+ handle = d->sectionHandleAt(pos);
+ if (!(handle > -1 && resizeMode(handle) == Interactive))
+ setCursor(Qt::ArrowCursor);
+ }
+#endif
+ } else {
+ emit sectionDoubleClicked(logicalIndexAt(e->pos()));
+ }
+}
+
+/*!
+ \reimp
+*/
+
+bool QHeaderView::viewportEvent(QEvent *e)
+{
+ Q_D(QHeaderView);
+ switch (e->type()) {
+#ifndef QT_NO_TOOLTIP
+ case QEvent::ToolTip: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
+ if (variant.isValid()) {
+ QToolTip::showText(he->globalPos(), variant.toString(), this);
+ return true;
+ }
+ }
+ break; }
+#endif
+#ifndef QT_NO_WHATSTHIS
+ case QEvent::QueryWhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1
+ && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
+ return true;
+ break; }
+ case QEvent::WhatsThis: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QVariant whatsthis = d->model->headerData(logical, d->orientation,
+ Qt::WhatsThisRole);
+ if (whatsthis.isValid()) {
+ QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
+ return true;
+ }
+ }
+ break; }
+#endif // QT_NO_WHATSTHIS
+#ifndef QT_NO_STATUSTIP
+ case QEvent::StatusTip: {
+ QHelpEvent *he = static_cast<QHelpEvent*>(e);
+ int logical = logicalIndexAt(he->pos());
+ if (logical != -1) {
+ QString statustip = d->model->headerData(logical, d->orientation,
+ Qt::StatusTipRole).toString();
+ if (!statustip.isEmpty())
+ setStatusTip(statustip);
+ }
+ return true; }
+#endif // QT_NO_STATUSTIP
+ case QEvent::Hide:
+ case QEvent::Show:
+ case QEvent::FontChange:
+ case QEvent::StyleChange:
+ d->invalidateCachedSizeHint();
+ resizeSections();
+ emit geometriesChanged();
+ break;
+ case QEvent::ContextMenu: {
+ d->state = QHeaderViewPrivate::NoState;
+ d->pressed = d->section = d->target = -1;
+ d->updateSectionIndicator(d->section, -1);
+ }
+ default:
+ break;
+ }
+ return QAbstractItemView::viewportEvent(e);
+}
+
+/*!
+ Paints the section specified by the given \a logicalIndex, using the given
+ \a painter and \a rect.
+
+ Normally, you do not have to call this function.
+*/
+
+void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ if (!rect.isValid())
+ return;
+ // get the state of the section
+ QStyleOptionHeader opt;
+ initStyleOption(&opt);
+ QStyle::State state = QStyle::State_None;
+ if (isEnabled())
+ state |= QStyle::State_Enabled;
+ if (window()->isActiveWindow())
+ state |= QStyle::State_Active;
+ if (d->clickableSections) {
+ if (logicalIndex == d->hover)
+ state |= QStyle::State_MouseOver;
+ if (logicalIndex == d->pressed)
+ state |= QStyle::State_Sunken;
+ else if (d->highlightSelected) {
+ if (d->sectionIntersectsSelection(logicalIndex))
+ state |= QStyle::State_On;
+ if (d->isSectionSelected(logicalIndex))
+ state |= QStyle::State_Sunken;
+ }
+
+ }
+ if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
+ opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
+ ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
+
+ // setup the style options structure
+ QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
+ Qt::TextAlignmentRole);
+ opt.rect = rect;
+ opt.section = logicalIndex;
+ opt.state |= state;
+ opt.textAlignment = Qt::Alignment(textAlignment.isValid()
+ ? Qt::Alignment(textAlignment.toInt())
+ : d->defaultAlignment);
+
+ opt.iconAlignment = Qt::AlignVCenter;
+ opt.text = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DisplayRole).toString();
+ if (d->textElideMode != Qt::ElideNone)
+ opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);
+
+ QVariant variant = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DecorationRole);
+ opt.icon = qvariant_cast<QIcon>(variant);
+ if (opt.icon.isNull())
+ opt.icon = qvariant_cast<QPixmap>(variant);
+ QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
+ Qt::ForegroundRole);
+ if (qVariantCanConvert<QBrush>(foregroundBrush))
+ opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
+
+ QPointF oldBO = painter->brushOrigin();
+ QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
+ Qt::BackgroundRole);
+ if (qVariantCanConvert<QBrush>(backgroundBrush)) {
+ opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
+ opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
+ painter->setBrushOrigin(opt.rect.topLeft());
+ }
+
+ // the section position
+ int visual = visualIndex(logicalIndex);
+ Q_ASSERT(visual != -1);
+ if (count() == 1)
+ opt.position = QStyleOptionHeader::OnlyOneSection;
+ else if (visual == 0)
+ opt.position = QStyleOptionHeader::Beginning;
+ else if (visual == count() - 1)
+ opt.position = QStyleOptionHeader::End;
+ else
+ opt.position = QStyleOptionHeader::Middle;
+ opt.orientation = d->orientation;
+ // the selected position
+ bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
+ bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
+ if (previousSelected && nextSelected)
+ opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
+ else if (previousSelected)
+ opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
+ else if (nextSelected)
+ opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
+ else
+ opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
+ // draw the section
+ style()->drawControl(QStyle::CE_Header, &opt, painter, this);
+
+ painter->setBrushOrigin(oldBO);
+}
+
+/*!
+ Returns the size of the contents of the section specified by the given
+ \a logicalIndex.
+
+ \sa defaultSectionSize()
+*/
+
+QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
+{
+ Q_D(const QHeaderView);
+ Q_ASSERT(logicalIndex >= 0);
+
+ // use SizeHintRole
+ QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
+ if (variant.isValid())
+ return qvariant_cast<QSize>(variant);
+
+ // otherwise use the contents
+ QStyleOptionHeader opt;
+ initStyleOption(&opt);
+ opt.section = logicalIndex;
+ QVariant var = d->model->headerData(logicalIndex, d->orientation,
+ Qt::FontRole);
+ QFont fnt;
+ if (var.isValid() && qVariantCanConvert<QFont>(var))
+ fnt = qvariant_cast<QFont>(var);
+ else
+ fnt = font();
+ fnt.setBold(true);
+ opt.fontMetrics = QFontMetrics(fnt);
+ opt.text = d->model->headerData(logicalIndex, d->orientation,
+ Qt::DisplayRole).toString();
+ variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
+ opt.icon = qvariant_cast<QIcon>(variant);
+ if (opt.icon.isNull())
+ opt.icon = qvariant_cast<QPixmap>(variant);
+ QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
+ if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) {
+ int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, &opt, this);
+ if (d->orientation == Qt::Horizontal)
+ size.rwidth() += size.height() + margin;
+ else
+ size.rheight() += size.width() + margin;
+ }
+ return size;
+}
+
+/*!
+ Returns the horizontal offset of the header. This is 0 for vertical
+ headers.
+
+ \sa offset()
+*/
+
+int QHeaderView::horizontalOffset() const
+{
+ Q_D(const QHeaderView);
+ if (d->orientation == Qt::Horizontal)
+ return d->offset;
+ return 0;
+}
+
+/*!
+ Returns the vertical offset of the header. This is 0 for horizontal
+ headers.
+
+ \sa offset()
+*/
+
+int QHeaderView::verticalOffset() const
+{
+ Q_D(const QHeaderView);
+ if (d->orientation == Qt::Vertical)
+ return d->offset;
+ return 0;
+}
+
+/*!
+ \reimp
+ \internal
+*/
+
+void QHeaderView::updateGeometries()
+{
+ Q_D(QHeaderView);
+ d->layoutChildren();
+ if (d->hasAutoResizeSections())
+ resizeSections();
+}
+
+/*!
+ \reimp
+ \internal
+*/
+
+void QHeaderView::scrollContentsBy(int dx, int dy)
+{
+ Q_D(QHeaderView);
+ d->scrollDirtyRegion(dx, dy);
+}
+
+/*!
+ \reimp
+ \internal
+*/
+void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+{
+ Q_D(QHeaderView);
+ d->invalidateCachedSizeHint();
+ if (d->hasAutoResizeSections()) {
+ bool resizeRequired = d->globalResizeMode == ResizeToContents;
+ int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
+ int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
+ for (int i = first; i <= last && !resizeRequired; ++i)
+ resizeRequired = (resizeRequired && resizeMode(i));
+ if (resizeRequired)
+ d->doDelayedResizeSections();
+ }
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+void QHeaderView::rowsInserted(const QModelIndex &, int, int)
+{
+ // do nothing
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QRect QHeaderView::visualRect(const QModelIndex &) const
+{
+ return QRect();
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
+{
+ // do nothing - the header only displays sections
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QModelIndex QHeaderView::indexAt(const QPoint &) const
+{
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+bool QHeaderView::isIndexHidden(const QModelIndex &) const
+{
+ return true; // the header view has no items, just sections
+}
+
+/*!
+ \reimp
+ \internal
+
+ Empty implementation because the header doesn't show QModelIndex items.
+*/
+
+QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
+{
+ return QModelIndex();
+}
+
+/*!
+ \reimp
+
+ Selects the items in the given \a rect according to the specified
+ \a flags.
+
+ The base class implementation does nothing.
+*/
+
+void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
+{
+ // do nothing
+}
+
+/*!
+ \internal
+*/
+
+QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
+{
+ Q_D(const QHeaderView);
+ const int max = d->modelSectionCount();
+ if (d->orientation == Qt::Horizontal) {
+ int left = max;
+ int right = 0;
+ int rangeLeft, rangeRight;
+
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange r = selection.at(i);
+ if (r.parent().isValid() || !r.isValid())
+ continue; // we only know about toplevel items and we don't want invalid ranges
+ // FIXME an item inside the range may be the leftmost or rightmost
+ rangeLeft = visualIndex(r.left());
+ if (rangeLeft == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ rangeRight = visualIndex(r.right());
+ if (rangeRight == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ if (rangeLeft < left)
+ left = rangeLeft;
+ if (rangeRight > right)
+ right = rangeRight;
+ }
+
+ int logicalLeft = logicalIndex(left);
+ int logicalRight = logicalIndex(right);
+
+ if (logicalLeft < 0 || logicalLeft >= count() ||
+ logicalRight < 0 || logicalRight >= count())
+ return QRegion();
+
+ int leftPos = sectionViewportPosition(logicalLeft);
+ int rightPos = sectionViewportPosition(logicalRight);
+ rightPos += sectionSize(logicalRight);
+ return QRect(leftPos, 0, rightPos - leftPos, height());
+ }
+ // orientation() == Qt::Vertical
+ int top = max;
+ int bottom = 0;
+ int rangeTop, rangeBottom;
+
+ for (int i = 0; i < selection.count(); ++i) {
+ QItemSelectionRange r = selection.at(i);
+ if (r.parent().isValid() || !r.isValid())
+ continue; // we only know about toplevel items
+ // FIXME an item inside the range may be the leftmost or rightmost
+ rangeTop = visualIndex(r.top());
+ if (rangeTop == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ rangeBottom = visualIndex(r.bottom());
+ if (rangeBottom == -1) // in some cases users may change the selections
+ continue; // before we have a chance to do the layout
+ if (rangeTop < top)
+ top = rangeTop;
+ if (rangeBottom > bottom)
+ bottom = rangeBottom;
+ }
+
+ int logicalTop = logicalIndex(top);
+ int logicalBottom = logicalIndex(bottom);
+
+ if (logicalTop == -1 || logicalBottom == -1)
+ return QRect();
+
+ int topPos = sectionViewportPosition(logicalTop);
+ int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
+
+ return QRect(0, topPos, width(), bottomPos - topPos);
+}
+
+
+// private implementation
+
+int QHeaderViewPrivate::sectionHandleAt(int position)
+{
+ Q_Q(QHeaderView);
+ int visual = q->visualIndexAt(position);
+ if (visual == -1)
+ return -1;
+ int log = logicalIndex(visual);
+ int pos = q->sectionViewportPosition(log);
+ int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, q);
+
+ bool atLeft = position < pos + grip;
+ bool atRight = (position > pos + q->sectionSize(log) - grip);
+ if (reverse())
+ qSwap(atLeft, atRight);
+
+ if (atLeft) {
+ //grip at the beginning of the section
+ while(visual > -1) {
+ int logical = q->logicalIndex(--visual);
+ if (!q->isSectionHidden(logical))
+ return logical;
+ }
+ } else if (atRight) {
+ //grip at the end of the section
+ return log;
+ }
+ return -1;
+}
+
+void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
+{
+ Q_Q(QHeaderView);
+ if (!sectionIndicator) {
+ sectionIndicator = new QLabel(viewport);
+ }
+
+ int x, y, w, h;
+ int p = q->sectionViewportPosition(section);
+ if (orientation == Qt::Horizontal) {
+ x = p;
+ y = 0;
+ w = q->sectionSize(section);
+ h = viewport->height();
+ } else {
+ x = 0;
+ y = p;
+ w = viewport->width();
+ h = q->sectionSize(section);
+ }
+ sectionIndicator->resize(w, h);
+
+ QPixmap pm(w, h);
+ pm.fill(QColor(0, 0, 0, 45));
+ QRect rect(0, 0, w, h);
+
+ QPainter painter(&pm);
+ painter.setOpacity(0.75);
+ q->paintSection(&painter, rect, section);
+ painter.end();
+
+ sectionIndicator->setPixmap(pm);
+ sectionIndicatorOffset = position - qMax(p, 0);
+}
+
+void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
+{
+ if (!sectionIndicator)
+ return;
+
+ if (section == -1 || target == -1) {
+ sectionIndicator->hide();
+ return;
+ }
+
+ if (orientation == Qt::Horizontal)
+ sectionIndicator->move(position - sectionIndicatorOffset, 0);
+ else
+ sectionIndicator->move(0, position - sectionIndicatorOffset);
+
+ sectionIndicator->show();
+}
+
+/*!
+ Initialize \a option with the values from this QHeaderView. This method is
+ useful for subclasses when they need a QStyleOptionHeader, but do not want
+ to fill in all the information themselves.
+
+ \sa QStyleOption::initFrom()
+*/
+void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
+{
+ Q_D(const QHeaderView);
+ option->initFrom(this);
+ option->state = QStyle::State_None | QStyle::State_Raised;
+ option->orientation = d->orientation;
+ if (d->orientation == Qt::Horizontal)
+ option->state |= QStyle::State_Horizontal;
+ if (isEnabled())
+ option->state |= QStyle::State_Enabled;
+ option->section = 0;
+}
+
+bool QHeaderViewPrivate::isSectionSelected(int section) const
+{
+ int i = section * 2;
+ if (i < 0 || i >= sectionSelected.count())
+ return false;
+ if (sectionSelected.testBit(i)) // if the value was cached
+ return sectionSelected.testBit(i + 1);
+ bool s = false;
+ if (orientation == Qt::Horizontal)
+ s = isColumnSelected(section);
+ else
+ s = isRowSelected(section);
+ sectionSelected.setBit(i + 1, s); // selection state
+ sectionSelected.setBit(i, true); // cache state
+ return s;
+}
+
+/*!
+ \internal
+ Returns the last visible (ie. not hidden) visual index
+*/
+int QHeaderViewPrivate::lastVisibleVisualIndex() const
+{
+ Q_Q(const QHeaderView);
+ for (int visual = q->count()-1; visual >= 0; --visual) {
+ if (!q->isSectionHidden(q->logicalIndex(visual)))
+ return visual;
+ }
+
+ //default value if no section is actually visible
+ return -1;
+}
+
+/*!
+ \internal
+ Go through and resize all of the sections applying stretchLastSection,
+ manualy stretches, sizes, and useGlobalMode.
+
+ The different resize modes are:
+ Interactive - the user decides the size
+ Stretch - take up whatever space is left
+ Fixed - the size is set programmatically outside the header
+ ResizeToContentes - the size is set based on the contents of the row or column in the parent view
+
+ The resize mode will not affect the last section if stretchLastSection is true.
+*/
+void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
+{
+ Q_Q(QHeaderView);
+
+ executePostedLayout();
+ if (sectionCount == 0)
+ return;
+ invalidateCachedSizeHint();
+
+ // find stretchLastSection if we have it
+ int stretchSection = -1;
+ if (stretchLastSection && !useGlobalMode) {
+ for (int i = sectionCount - 1; i >= 0; --i) {
+ if (!isVisualIndexHidden(i)) {
+ stretchSection = i;
+ break;
+ }
+ }
+ }
+
+ // count up the number of strected sections and how much space left for them
+ int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
+ int numberOfStretchedSections = 0;
+ QList<int> section_sizes;
+ for (int i = 0; i < sectionCount; ++i) {
+ if (isVisualIndexHidden(i))
+ continue;
+
+ QHeaderView::ResizeMode resizeMode;
+ if (useGlobalMode && (i != stretchSection))
+ resizeMode = globalMode;
+ else
+ resizeMode = (i == stretchSection ? QHeaderView::Stretch : visualIndexResizeMode(i));
+
+ if (resizeMode == QHeaderView::Stretch) {
+ ++numberOfStretchedSections;
+ section_sizes.append(headerSectionSize(i));
+ continue;
+ }
+
+ // because it isn't stretch, determine its width and remove that from lengthToStrech
+ int sectionSize = 0;
+ if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
+ sectionSize = headerSectionSize(i);
+ } else { // resizeMode == QHeaderView::ResizeToContents
+ int logicalIndex = q->logicalIndex(i);
+ sectionSize = qMax(viewSectionSizeHint(logicalIndex),
+ q->sectionSizeHint(logicalIndex));
+ }
+ section_sizes.append(sectionSize);
+ lengthToStrech -= sectionSize;
+ }
+
+ // calculate the new length for all of the stretched sections
+ int stretchSectionLength = -1;
+ int pixelReminder = 0;
+ if (numberOfStretchedSections > 0 && lengthToStrech > 0) { // we have room to stretch in
+ int hintLengthForEveryStretchedSection = lengthToStrech / numberOfStretchedSections;
+ stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
+ pixelReminder = lengthToStrech % numberOfStretchedSections;
+ }
+
+ int spanStartSection = 0;
+ int previousSectionLength = 0;
+ const int lastVisibleSection = lastVisibleVisualIndex();
+
+ QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
+
+ // resize each section along the total length
+ for (int i = 0; i < sectionCount; ++i) {
+ int oldSectionLength = headerSectionSize(i);
+ int newSectionLength = -1;
+ QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
+
+ if (isVisualIndexHidden(i)) {
+ newSectionLength = 0;
+ } else {
+ QHeaderView::ResizeMode resizeMode;
+ if (useGlobalMode)
+ resizeMode = globalMode;
+ else
+ resizeMode = (i == stretchSection
+ ? QHeaderView::Stretch
+ : visualIndexResizeMode(i));
+ if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
+ if (i == lastVisibleSection)
+ newSectionLength = qMax(stretchSectionLength, lastSectionSize);
+ else
+ newSectionLength = stretchSectionLength;
+ if (pixelReminder > 0) {
+ newSectionLength += 1;
+ --pixelReminder;
+ }
+ section_sizes.removeFirst();
+ } else {
+ newSectionLength = section_sizes.front();
+ section_sizes.removeFirst();
+ }
+ }
+
+ //Q_ASSERT(newSectionLength > 0);
+ if ((previousSectionResizeMode != newSectionResizeMode
+ || previousSectionLength != newSectionLength) && i > 0) {
+ int spanLength = (i - spanStartSection) * previousSectionLength;
+ createSectionSpan(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
+ //Q_ASSERT(headerLength() == length);
+ spanStartSection = i;
+ }
+
+ if (newSectionLength != oldSectionLength)
+ emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
+
+ previousSectionLength = newSectionLength;
+ previousSectionResizeMode = newSectionResizeMode;
+ }
+
+ createSectionSpan(spanStartSection, sectionCount - 1,
+ (sectionCount - spanStartSection) * previousSectionLength,
+ previousSectionResizeMode);
+ //Q_ASSERT(headerLength() == length);
+
+ viewport->update();
+}
+
+void QHeaderViewPrivate::createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode)
+{
+ // ### the code for merging spans does not merge at all opertuneties
+ // ### what if the number of sections is reduced ?
+
+ SectionSpan span(size, (end - start) + 1, mode);
+ int start_section = 0;
+#ifndef QT_NO_DEBUG
+ int initial_section_count = headerSectionCount(); // ### debug code
+#endif
+
+ QList<int> spansToRemove;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ int end_section = start_section + sectionSpans.at(i).count - 1;
+ int section_count = sectionSpans.at(i).count;
+ if (start <= start_section && end > end_section) {
+ // the existing span is entirely coveded by the new span
+ spansToRemove.append(i);
+ } else if (start < start_section && end >= end_section) {
+ // the existing span is entirely coveded by the new span
+ spansToRemove.append(i);
+ } else if (start == start_section && end == end_section) {
+ // the new span is covered by an existin span
+ length -= sectionSpans.at(i).size;
+ length += size;
+ sectionSpans[i].size = size;
+ sectionSpans[i].resizeMode = mode;
+ // ### check if we can merge the section with any of its neighbours
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ } else if (start > start_section && end < end_section) {
+ if (sectionSpans.at(i).sectionSize() == span.sectionSize()
+ && sectionSpans.at(i).resizeMode == span.resizeMode) {
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ // the new span is in the middle of the old span, so we have to split it
+ length -= sectionSpans.at(i).size;
+ int section_size = sectionSpans.at(i).sectionSize();
+#ifndef QT_NO_DEBUG
+ int span_count = sectionSpans.at(i).count;
+#endif
+ QHeaderView::ResizeMode span_mode = sectionSpans.at(i).resizeMode;
+ // first span
+ int first_span_count = start - start_section;
+ int first_span_size = section_size * first_span_count;
+ sectionSpans[i].count = first_span_count;
+ sectionSpans[i].size = first_span_size;
+ sectionSpans[i].resizeMode = span_mode;
+ length += first_span_size;
+ // middle span (the new span)
+#ifndef QT_NO_DEBUG
+ int mid_span_count = span.count;
+#endif
+ int mid_span_size = span.size;
+ sectionSpans.insert(i + 1, span);
+ length += mid_span_size;
+ // last span
+ int last_span_count = end_section - end;
+ int last_span_size = section_size * last_span_count;
+ sectionSpans.insert(i + 2, SectionSpan(last_span_size, last_span_count, span_mode));
+ length += last_span_size;
+ Q_ASSERT(span_count == first_span_count + mid_span_count + last_span_count);
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ } else if (start > start_section && start <= end_section && end >= end_section) {
+ // the new span covers the last part of the existing span
+ length -= sectionSpans.at(i).size;
+ int removed_count = (end_section - start + 1);
+ int span_count = sectionSpans.at(i).count - removed_count;
+ int section_size = sectionSpans.at(i).sectionSize();
+ int span_size = section_size * span_count;
+ sectionSpans[i].count = span_count;
+ sectionSpans[i].size = span_size;
+ length += span_size;
+ if (end == end_section) {
+ sectionSpans.insert(i + 1, span); // insert after
+ length += span.size;
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ } else if (end < end_section && end >= start_section && start <= start_section) {
+ // the new span covers the first part of the existing span
+ length -= sectionSpans.at(i).size;
+ int removed_count = (end - start_section + 1);
+ int section_size = sectionSpans.at(i).sectionSize();
+ int span_count = sectionSpans.at(i).count - removed_count;
+ int span_size = section_size * span_count;
+ sectionSpans[i].count = span_count;
+ sectionSpans[i].size = span_size;
+ length += span_size;
+ sectionSpans.insert(i, span); // insert before
+ length += span.size;
+ removeSpans(spansToRemove);
+ Q_ASSERT(initial_section_count == headerSectionCount());
+ return;
+ }
+ start_section += section_count;
+ }
+
+ // ### adding and removing _ sections_ in addition to spans
+ // ### add some more checks here
+
+ if (spansToRemove.isEmpty()) {
+ if (!sectionSpans.isEmpty()
+ && sectionSpans.last().sectionSize() == span.sectionSize()
+ && sectionSpans.last().resizeMode == span.resizeMode) {
+ length += span.size;
+ int last = sectionSpans.count() - 1;
+ sectionSpans[last].count += span.count;
+ sectionSpans[last].size += span.size;
+ sectionSpans[last].resizeMode = span.resizeMode;
+ } else {
+ length += span.size;
+ sectionSpans.append(span);
+ }
+ } else {
+ removeSpans(spansToRemove);
+ length += span.size;
+ sectionSpans.insert(spansToRemove.first(), span);
+ //Q_ASSERT(initial_section_count == headerSectionCount());
+ }
+}
+
+void QHeaderViewPrivate::removeSectionsFromSpans(int start, int end)
+{
+ // remove sections
+ int start_section = 0;
+ QList<int> spansToRemove;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ int end_section = start_section + sectionSpans.at(i).count - 1;
+ int section_size = sectionSpans.at(i).sectionSize();
+ int section_count = sectionSpans.at(i).count;
+ if (start <= start_section && end >= end_section) {
+ // the change covers the entire span
+ spansToRemove.append(i);
+ if (end == end_section)
+ break;
+ } else if (start > start_section && end < end_section) {
+ // all the removed sections are inside the span
+ int change = (end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ length -= (change * section_size);
+ break;
+ } else if (start >= start_section && start <= end_section) {
+ // the some of the removed sections are inside the span,at the end
+ int change = qMin(end_section - start + 1, end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ start += change;
+ length -= (change * section_size);
+ // the change affects several spans
+ } else if (end >= start_section && end <= end_section) {
+ // the some of the removed sections are inside the span, at the beginning
+ int change = qMin((end - start_section + 1), end - start + 1);
+ sectionSpans[i].count -= change;
+ sectionSpans[i].size = section_size * sectionSpans.at(i).count;
+ length -= (change * section_size);
+ break;
+ }
+ start_section += section_count;
+ }
+
+ for (int i = spansToRemove.count() - 1; i >= 0; --i) {
+ int s = spansToRemove.at(i);
+ length -= sectionSpans.at(s).size;
+ sectionSpans.remove(s);
+ // ### merge remaining spans
+ }
+}
+
+void QHeaderViewPrivate::clear()
+{
+ if (state != NoClear) {
+ length = 0;
+ sectionCount = 0;
+ visualIndices.clear();
+ logicalIndices.clear();
+ sectionSelected.clear();
+ sectionHidden.clear();
+ hiddenSectionSize.clear();
+ sectionSpans.clear();
+ }
+}
+
+void QHeaderViewPrivate::flipSortIndicator(int section)
+{
+ Q_Q(QHeaderView);
+ bool ascending = (sortIndicatorSection != section
+ || sortIndicatorOrder == Qt::DescendingOrder);
+ q->setSortIndicator(section, ascending ? Qt::AscendingOrder : Qt::DescendingOrder);
+}
+
+void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
+{
+ Q_Q(QHeaderView);
+ const int minimumSize = q->minimumSectionSize();
+ const int oldSize = headerSectionSize(visual);
+ int delta = newSize - oldSize;
+
+ if (delta > 0) { // larger
+ bool sectionResized = false;
+
+ // restore old section sizes
+ for (int i = firstCascadingSection; i < visual; ++i) {
+ if (cascadingSectionSize.contains(i)) {
+ int currentSectionSize = headerSectionSize(i);
+ int originalSectionSize = cascadingSectionSize.value(i);
+ if (currentSectionSize < originalSectionSize) {
+ int newSectionSize = currentSectionSize + delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ if (newSectionSize >= originalSectionSize && false)
+ cascadingSectionSize.remove(i); // the section is now restored
+ sectionResized = true;
+ break;
+ }
+ }
+
+ }
+
+ // resize the section
+ if (!sectionResized) {
+ newSize = qMax(newSize, minimumSize);
+ if (oldSize != newSize)
+ resizeSectionSpan(visual, oldSize, newSize);
+ }
+
+ // cascade the section size change
+ for (int i = visual + 1; i < sectionCount; ++i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ if (currentSectionSize <= minimumSize)
+ continue;
+ int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
+ //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ saveCascadingSectionSize(i, currentSectionSize);
+ delta = delta - (currentSectionSize - newSectionSize);
+ //qDebug() << "new delta" << delta;
+ //if (newSectionSize != minimumSize)
+ if (delta <= 0)
+ break;
+ }
+ } else { // smaller
+ bool sectionResized = false;
+
+ // restore old section sizes
+ for (int i = lastCascadingSection; i > visual; --i) {
+ if (!cascadingSectionSize.contains(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ int originalSectionSize = cascadingSectionSize.value(i);
+ if (currentSectionSize >= originalSectionSize)
+ continue;
+ int newSectionSize = currentSectionSize - delta;
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ if (newSectionSize >= originalSectionSize && false) {
+ //qDebug() << "section" << i << "restored to" << originalSectionSize;
+ cascadingSectionSize.remove(i); // the section is now restored
+ }
+ sectionResized = true;
+ break;
+ }
+
+ // resize the section
+ resizeSectionSpan(visual, oldSize, qMax(newSize, minimumSize));
+
+ // cascade the section size change
+ if (delta < 0 && newSize < minimumSize) {
+ for (int i = visual - 1; i >= 0; --i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int sectionSize = headerSectionSize(i);
+ if (sectionSize <= minimumSize)
+ continue;
+ resizeSectionSpan(i, sectionSize, qMax(sectionSize + delta, minimumSize));
+ saveCascadingSectionSize(i, sectionSize);
+ break;
+ }
+ }
+
+ // let the next section get the space from the resized section
+ if (!sectionResized) {
+ for (int i = visual + 1; i < sectionCount; ++i) {
+ if (!sectionIsCascadable(i))
+ continue;
+ int currentSectionSize = headerSectionSize(i);
+ int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
+ resizeSectionSpan(i, currentSectionSize, newSectionSize);
+ break;
+ }
+ }
+ }
+
+ if (hasAutoResizeSections())
+ doDelayedResizeSections();
+
+ viewport->update();
+}
+
+void QHeaderViewPrivate::resizeSectionSpan(int visualIndex, int oldSize, int newSize)
+{
+ Q_Q(QHeaderView);
+ QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
+ createSectionSpan(visualIndex, visualIndex, newSize, mode);
+ emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
+}
+
+int QHeaderViewPrivate::headerSectionSize(int visual) const
+{
+ // ### stupid iteration
+ int section_start = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int section_end = section_start + currentSection.count - 1;
+ if (visual >= section_start && visual <= section_end)
+ return currentSection.sectionSize();
+ section_start = section_end + 1;
+ }
+ return -1;
+}
+
+int QHeaderViewPrivate::headerSectionPosition(int visual) const
+{
+ // ### stupid iteration
+ int section_start = 0;
+ int span_position = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int section_end = section_start + currentSection.count - 1;
+ if (visual >= section_start && visual <= section_end)
+ return span_position + (visual - section_start) * currentSection.sectionSize();
+ section_start = section_end + 1;
+ span_position += currentSection.size;
+ }
+ return -1;
+}
+
+int QHeaderViewPrivate::headerVisualIndexAt(int position) const
+{
+ // ### stupid iteration
+ int span_start_section = 0;
+ int span_position = 0;
+ const int sectionSpansCount = sectionSpans.count();
+ for (int i = 0; i < sectionSpansCount; ++i) {
+ const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
+ int next_span_start_section = span_start_section + currentSection.count;
+ int next_span_position = span_position + currentSection.size;
+ if (position == span_position)
+ return span_start_section; // spans with no size
+ if (position > span_position && position < next_span_position) {
+ int position_in_span = position - span_position;
+ return span_start_section + (position_in_span / currentSection.sectionSize());
+ }
+ span_start_section = next_span_start_section;
+ span_position = next_span_position;
+ }
+ return -1;
+}
+
+void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
+{
+ int size = headerSectionSize(visual);
+ createSectionSpan(visual, visual, size, mode);
+}
+
+QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
+{
+ int span = sectionSpanIndex(visual);
+ if (span == -1)
+ return globalResizeMode;
+ return sectionSpans.at(span).resizeMode;
+}
+
+void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
+{
+ globalResizeMode = mode;
+ for (int i = 0; i < sectionSpans.count(); ++i)
+ sectionSpans[i].resizeMode = mode;
+}
+
+int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
+{
+ Q_Q(const QHeaderView);
+ if (QAbstractItemView *parent = qobject_cast<QAbstractItemView*>(q->parent())) {
+ return (orientation == Qt::Horizontal
+ ? parent->sizeHintForColumn(logical)
+ : parent->sizeHintForRow(logical));
+ }
+ return 0;
+}
+
+int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
+{
+ if (hiddenSectionSize.count() > 0) {
+ int adjustedVisualIndex = visualIndex;
+ int currentVisualIndex = 0;
+ for (int i = 0; i < sectionSpans.count(); ++i) {
+ if (sectionSpans.at(i).size == 0)
+ adjustedVisualIndex += sectionSpans.at(i).count;
+ else
+ currentVisualIndex += sectionSpans.at(i).count;
+ if (currentVisualIndex >= visualIndex)
+ break;
+ }
+ visualIndex = adjustedVisualIndex;
+ }
+ return visualIndex;
+}
+
+#ifndef QT_NO_DATASTREAM
+void QHeaderViewPrivate::write(QDataStream &out) const
+{
+ out << int(orientation);
+ out << int(sortIndicatorOrder);
+ out << sortIndicatorSection;
+ out << sortIndicatorShown;
+
+ out << visualIndices;
+ out << logicalIndices;
+
+ out << sectionHidden;
+ out << hiddenSectionSize;
+
+ out << length;
+ out << sectionCount;
+ out << movableSections;
+ out << clickableSections;
+ out << highlightSelected;
+ out << stretchLastSection;
+ out << cascadingResizing;
+ out << stretchSections;
+ out << contentsSections;
+ out << defaultSectionSize;
+ out << minimumSectionSize;
+
+ out << int(defaultAlignment);
+ out << int(globalResizeMode);
+
+ out << sectionSpans;
+}
+
+bool QHeaderViewPrivate::read(QDataStream &in)
+{
+ int orient, order, align, global;
+ in >> orient;
+ orientation = (Qt::Orientation)orient;
+
+ in >> order;
+ sortIndicatorOrder = (Qt::SortOrder)order;
+
+ in >> sortIndicatorSection;
+ in >> sortIndicatorShown;
+
+ in >> visualIndices;
+ in >> logicalIndices;
+
+ in >> sectionHidden;
+ in >> hiddenSectionSize;
+
+ in >> length;
+ in >> sectionCount;
+ in >> movableSections;
+ in >> clickableSections;
+ in >> highlightSelected;
+ in >> stretchLastSection;
+ in >> cascadingResizing;
+ in >> stretchSections;
+ in >> contentsSections;
+ in >> defaultSectionSize;
+ in >> minimumSectionSize;
+
+ in >> align;
+ defaultAlignment = (Qt::Alignment)align;
+
+ in >> global;
+ globalResizeMode = (QHeaderView::ResizeMode)global;
+
+ in >> sectionSpans;
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_DATASTREAEM
+
+#endif // QT_NO_ITEMVIEWS
+
+#include "moc_qheaderview.cpp"