diff options
Diffstat (limited to 'src/gui/itemviews/qheaderview.cpp')
-rw-r--r-- | src/gui/itemviews/qheaderview.cpp | 3558 |
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..27c2c54 --- /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 ¤t, 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 ¤tSection = 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 ¤tSection = 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 ¤tSection = 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" |