| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWidgets module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qheaderview.h" |
| |
| #include <qbitarray.h> |
| #include <qbrush.h> |
| #include <qdebug.h> |
| #include <qevent.h> |
| #include <qpainter.h> |
| #include <qscrollbar.h> |
| #include <qtooltip.h> |
| #if QT_CONFIG(whatsthis) |
| #include <qwhatsthis.h> |
| #endif |
| #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> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| #ifndef QT_NO_DATASTREAM |
| QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionItem §ion) |
| { |
| section.write(out); |
| return out; |
| } |
| |
| QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionItem §ion) |
| { |
| section.read(in); |
| return in; |
| } |
| #endif // QT_NO_DATASTREAM |
| |
| static const int maxSizeSection = 1048575; // since section size is in a bitfield (uint 20). See qheaderview_p.h |
| // if this is changed then the docs in maximumSectionSize should be changed. |
| |
| /*! |
| \class QHeaderView |
| |
| \brief The QHeaderView class provides a header row or header column for |
| item views. |
| |
| \ingroup model-view |
| \inmodule QtWidgets |
| |
| 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 setSectionsMovable(). It can |
| be made clickable with setSectionsClickable(), and has resizing behavior in |
| accordance with setSectionResizeMode(). |
| |
| \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(). |
| |
| 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, unless they are |
| in conflict with the style (which can happen for styles that follow |
| the desktop theme): |
| |
| \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 |
| setSectionResizeMode(). |
| |
| \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 setSectionResizeMode(), 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 setSectionsClickable() |
| */ |
| |
| /*! |
| \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 setSectionsClickable(), 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 setSectionsClickable(), 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 setSectionsClickable() |
| */ |
| |
| /*! |
| \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 setSectionsClickable() |
| */ |
| |
| /*! |
| \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::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 \c 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); |
| d->layoutChangePersistentSections.clear(); |
| 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))); |
| QObject::disconnect(d->model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); |
| QObject::disconnect(d->model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,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(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); |
| QObject::disconnect(d->model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int))); |
| } |
| QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), |
| this, SLOT(headerDataChanged(Qt::Orientation,int,int))); |
| QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), |
| this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); |
| QObject::disconnect(d->model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), |
| this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); |
| } |
| |
| 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))); |
| QObject::connect(model, SIGNAL(columnsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); |
| QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,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(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); |
| QObject::connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), |
| this, SLOT(_q_sectionsMoved(QModelIndex,int,int,QModelIndex,int))); |
| } |
| QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)), |
| this, SLOT(headerDataChanged(Qt::Orientation,int,int))); |
| QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), |
| this, SLOT(_q_sectionsAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); |
| QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), |
| this, SLOT(_q_sectionsChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); |
| } |
| |
| 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 && !d->preventCursorChangeInSetOffset) { |
| 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 visualSectionNumber. |
| \a visualSectionNumber is the actual visible section when hiddenSections are |
| not considered. That is not always the same as visualIndex(). |
| |
| \sa setOffset(), sectionPosition() |
| */ |
| void QHeaderView::setOffsetToSectionPosition(int visualSectionNumber) |
| { |
| Q_D(QHeaderView); |
| if (visualSectionNumber > -1 && visualSectionNumber < d->sectionCount()) { |
| int position = d->headerSectionPosition(d->adjustedVisualIndex(visualSectionNumber)); |
| 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(), setSectionResizeMode(), offset() |
| */ |
| |
| int QHeaderView::length() const |
| { |
| Q_D(const QHeaderView); |
| d->executePostedLayout(); |
| d->executePostedResize(); |
| //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 (d->cachedSizeHint.isValid()) |
| return d->cachedSizeHint; |
| d->cachedSizeHint = QSize(0, 0); //reinitialize the cached size hint |
| const int sectionCount = count(); |
| |
| // get size hint for the first n sections |
| int i = 0; |
| for (int checked = 0; checked < 100 && i < sectionCount; ++i) { |
| if (isSectionHidden(i)) |
| continue; |
| checked++; |
| QSize hint = sectionSizeFromContents(i); |
| d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint); |
| } |
| // get size hint for the last n sections |
| i = qMax(i, sectionCount - 100 ); |
| for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) { |
| if (isSectionHidden(j)) |
| continue; |
| checked++; |
| QSize hint = sectionSizeFromContents(j); |
| d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint); |
| } |
| return d->cachedSizeHint; |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| void QHeaderView::setVisible(bool v) |
| { |
| bool actualChange = (v != isVisible()); |
| QAbstractItemView::setVisible(v); |
| if (actualChange) { |
| QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea*>(parentWidget()); |
| if (parent) |
| parent->updateGeometry(); |
| } |
| } |
| |
| |
| /*! |
| Returns a suitable size hint for the section specified by \a logicalIndex. |
| |
| \sa sizeHint(), defaultSectionSize(), minimumSectionSize(), maximumSectionSize() |
| 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 qBound(minimumSectionSize(), hint, maximumSectionSize()); |
| } |
| |
| /*! |
| 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 - 1; |
| 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(), setSectionResizeMode(), 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. The position is measured in pixels from |
| the first visible item's top-left corner to the top-left corner of |
| the item with \a logicalIndex. The measurement is along the x-axis |
| for horizontal headers and along the y-axis for vertical headers. |
| |
| \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() |
| */ |
| |
| template<typename Container> |
| static void qMoveRange(Container& c, |
| typename Container::size_type rangeStart, |
| typename Container::size_type rangeEnd, |
| typename Container::size_type targetPosition) |
| { |
| Q_ASSERT(targetPosition <= c.size()); |
| Q_ASSERT(targetPosition < rangeStart || targetPosition >= rangeEnd); |
| |
| const bool forwardMove = targetPosition > rangeStart; |
| typename Container::size_type first = std::min(rangeStart, targetPosition); |
| typename Container::size_type mid = forwardMove ? rangeEnd : rangeStart; |
| typename Container::size_type last = forwardMove ? targetPosition + 1 : rangeEnd; |
| std::rotate(c.begin() + first, c.begin() + mid, c.begin() + last); |
| } |
| |
| /*! |
| 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; |
| } |
| |
| d->initializeIndexMapping(); |
| |
| int *visualIndices = d->visualIndices.data(); |
| int *logicalIndices = d->logicalIndices.data(); |
| int logical = logicalIndices[from]; |
| int visual = from; |
| |
| if (to > from) { |
| while (visual < to) { |
| visualIndices[logicalIndices[visual + 1]] = visual; |
| logicalIndices[visual] = logicalIndices[visual + 1]; |
| ++visual; |
| } |
| } else { |
| while (visual > to) { |
| visualIndices[logicalIndices[visual - 1]] = visual; |
| logicalIndices[visual] = logicalIndices[visual - 1]; |
| --visual; |
| } |
| } |
| visualIndices[logical] = to; |
| logicalIndices[to] = logical; |
| |
| qMoveRange(d->sectionItems, from, from + 1, to); |
| |
| d->sectionStartposRecalc = true; |
| |
| if (d->hasAutoResizeSections()) |
| d->doDelayedResizeSections(); |
| d->viewport->update(); |
| |
| emit sectionMoved(logical, from, to); |
| |
| if (stretchLastSection()) { |
| const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx); |
| if (from >= lastSectionVisualIdx || to >= lastSectionVisualIdx) |
| d->maybeRestorePrevLastSectionAndStretchLast(); |
| } |
| } |
| |
| /*! |
| \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); |
| |
| if (d->state == QHeaderViewPrivate::ResizeSection) |
| d->preventCursorChangeInSetOffset = true; |
| |
| d->createSectionItems(second, second, firstSize, firstMode); |
| d->createSectionItems(first, first, secondSize, secondMode); |
| |
| d->initializeIndexMapping(); |
| |
| d->visualIndices[firstLogical] = second; |
| d->logicalIndices[second] = firstLogical; |
| |
| d->visualIndices[secondLogical] = first; |
| d->logicalIndices[first] = secondLogical; |
| |
| if (!d->hiddenSectionSize.isEmpty()) { |
| bool firstHidden = d->isVisualIndexHidden(first); |
| bool secondHidden = d->isVisualIndexHidden(second); |
| d->setVisualIndexHidden(first, secondHidden); |
| d->setVisualIndexHidden(second, firstHidden); |
| } |
| |
| d->viewport->update(); |
| emit sectionMoved(firstLogical, first, second); |
| emit sectionMoved(secondLogical, second, first); |
| |
| if (stretchLastSection()) { |
| const int lastSectionVisualIdx = visualIndex(d->lastSectionLogicalIdx); |
| if (first >= lastSectionVisualIdx || second >= lastSectionVisualIdx) |
| d->maybeRestorePrevLastSectionAndStretchLast(); |
| } |
| } |
| |
| /*! |
| \fn void QHeaderView::resizeSection(int logicalIndex, int size) |
| |
| Resizes the section specified by \a logicalIndex to \a size measured in |
| pixels. The size parameter must be a value larger or equal to zero. A |
| size equal to zero is however not recommended. In that situation hideSection |
| should be used instead. |
| |
| \sa sectionResized(), sectionSize(), hideSection() |
| */ |
| |
| void QHeaderView::resizeSection(int logical, int size) |
| { |
| Q_D(QHeaderView); |
| if (logical < 0 || logical >= count() || size < 0 || size > maxSizeSection) |
| return; |
| |
| // make sure to not exceed bounds when setting size programmatically |
| if (size > 0) |
| size = qBound(minimumSectionSize(), size, maximumSectionSize()); |
| |
| if (isSectionHidden(logical)) { |
| d->hiddenSectionSize.insert(logical, size); |
| return; |
| } |
| |
| int visual = visualIndex(logical); |
| if (visual == -1) |
| return; |
| |
| if (d->state == QHeaderViewPrivate::ResizeSection && !d->cascadingResizing && logical != d->section) |
| d->preventCursorChangeInSetOffset = true; |
| |
| int oldSize = d->headerSectionSize(visual); |
| if (oldSize == size) |
| return; |
| |
| d->executePostedLayout(); |
| d->invalidateCachedSizeHint(); |
| |
| if (stretchLastSection() && logical == d->lastSectionLogicalIdx) |
| d->lastSectionSize = size; |
| |
| d->createSectionItems(visual, visual, size, d->headerSectionResizeMode(visual)); |
| |
| if (!updatesEnabled()) { |
| if (d->hasAutoResizeSections()) |
| d->doDelayedResizeSections(); |
| emit sectionResized(logical, oldSize, size); |
| return; |
| } |
| |
| 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(); |
| } |
| |
| // If the parent is a QAbstractScrollArea with QAbstractScrollArea::AdjustToContents |
| // then we want to change the geometry on that widget. Not doing it at once can/will |
| // cause scrollbars flicker as they would be shown at first but then removed. |
| // In the same situation it will also allow shrinking the whole view when stretchLastSection is set |
| // (It is default on QTreeViews - and it wouldn't shrink since the last stretch was made before the |
| // viewport was resized) |
| |
| QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget()); |
| if (parent && parent->sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents) |
| parent->updateGeometry(); |
| |
| 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 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 \c true if the section specified by \a logicalIndex is explicitly |
| hidden from the user; otherwise returns \c false. |
| |
| \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount() |
| */ |
| |
| bool QHeaderView::isSectionHidden(int logicalIndex) const |
| { |
| Q_D(const QHeaderView); |
| d->executePostedLayout(); |
| if (d->hiddenSectionSize.isEmpty() || logicalIndex < 0 || logicalIndex >= d->sectionCount()) |
| return false; |
| int visual = visualIndex(logicalIndex); |
| Q_ASSERT(visual != -1); |
| return d->isVisualIndexHidden(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) { |
| const bool isHidingLastSection = (stretchLastSection() && logicalIndex == d->lastSectionLogicalIdx); |
| if (isHidingLastSection) |
| d->restoreSizeOnPrevLastSection(); // Restore here/now to get the right restore size. |
| int size = d->headerSectionSize(visual); |
| if (!d->hasAutoResizeSections()) |
| resizeSection(logicalIndex, 0); |
| d->hiddenSectionSize.insert(logicalIndex, size); |
| d->setVisualIndexHidden(visual, true); |
| if (isHidingLastSection) |
| d->setNewLastSection(d->lastVisibleVisualIndex()); |
| if (d->hasAutoResizeSections()) |
| d->doDelayedResizeSections(); |
| } else { |
| int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize); |
| d->hiddenSectionSize.remove(logicalIndex); |
| d->setVisualIndexHidden(visual, false); |
| resizeSection(logicalIndex, size); |
| |
| const bool newLastSection = (stretchLastSection() && visual > visualIndex(d->lastSectionLogicalIdx)); |
| if (newLastSection) { |
| d->restoreSizeOnPrevLastSection(); |
| d->setNewLastSection(visual); |
| } |
| } |
| } |
| |
| /*! |
| 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 if visualIndex < 0 or visualIndex >= QHeaderView::count(). |
| |
| Note that the visualIndex is not affected by hidden sections. |
| |
| \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); |
| } |
| |
| /*! |
| \since 5.0 |
| |
| If \a movable is true, the header sections may be moved by the user; |
| otherwise they are fixed in place. |
| |
| When used in combination with QTreeView, the first column is not |
| movable (since it contains the tree structure), by default. |
| You can make it movable with setFirstSectionMovable(true). |
| |
| \sa sectionsMovable(), sectionMoved() |
| \sa setFirstSectionMovable() |
| */ |
| |
| void QHeaderView::setSectionsMovable(bool movable) |
| { |
| Q_D(QHeaderView); |
| d->movableSections = movable; |
| } |
| |
| /*! |
| \since 5.0 |
| |
| Returns \c true if the header can be moved by the user; otherwise returns |
| false. |
| |
| By default, sections are movable in QTreeView (except for the first one), |
| and not movable in QTableView. |
| |
| \sa setSectionsMovable() |
| */ |
| |
| bool QHeaderView::sectionsMovable() const |
| { |
| Q_D(const QHeaderView); |
| return d->movableSections; |
| } |
| |
| /*! |
| \property QHeaderView::firstSectionMovable |
| \brief Whether the first column can be moved by the user |
| |
| This property controls whether the first column can be moved by the user. |
| In a QTreeView, the first column holds the tree structure and is |
| therefore non-movable by default, even after setSectionsMovable(true). |
| |
| It can be made movable again, for instance in the case of flat lists |
| without a tree structure, by calling this method. |
| In such a scenario, it is recommended to call QTreeView::setRootIsDecorated(false) |
| as well. |
| |
| Setting it to true has no effect unless setSectionsMovable(true) is called |
| as well. |
| |
| \sa setSectionsMovable() |
| \since 5.11 |
| */ |
| void QHeaderView::setFirstSectionMovable(bool movable) |
| { |
| Q_D(QHeaderView); |
| d->allowUserMoveOfSection0 = movable; |
| } |
| |
| bool QHeaderView::isFirstSectionMovable() const |
| { |
| Q_D(const QHeaderView); |
| return d->allowUserMoveOfSection0; |
| } |
| |
| /*! |
| \since 5.0 |
| |
| If \a clickable is true, the header will respond to single clicks. |
| |
| \sa sectionsClickable(), sectionClicked(), sectionPressed(), |
| setSortIndicatorShown() |
| */ |
| |
| void QHeaderView::setSectionsClickable(bool clickable) |
| { |
| Q_D(QHeaderView); |
| d->clickableSections = clickable; |
| } |
| |
| /*! |
| \since 5.0 |
| |
| Returns \c true if the header is clickable; otherwise returns \c 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 setSectionsClickable() |
| */ |
| |
| bool QHeaderView::sectionsClickable() 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; |
| } |
| |
| /*! |
| \since 5.0 |
| |
| Sets the constraints on how the header can be resized to those described |
| by the given \a mode. |
| |
| \sa length(), sectionResized() |
| */ |
| |
| void QHeaderView::setSectionResizeMode(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 |
| } |
| |
| /*! |
| \since 5.0 |
| |
| 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. The logical |
| index should exist at the time this function is called. |
| |
| \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(), resizeContentsPrecision() |
| */ |
| |
| void QHeaderView::setSectionResizeMode(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 |
| } |
| |
| /*! |
| \since 5.0 |
| |
| Returns the resize mode that applies to the section specified by the given |
| \a logicalIndex. |
| |
| \sa setSectionResizeMode() |
| */ |
| |
| QHeaderView::ResizeMode QHeaderView::sectionResizeMode(int logicalIndex) const |
| { |
| Q_D(const QHeaderView); |
| int visual = visualIndex(logicalIndex); |
| if (visual == -1) |
| return Fixed; //the default value |
| return d->headerSectionResizeMode(visual); |
| } |
| |
| /*! |
| \since 5.2 |
| Sets how precise QHeaderView should calculate the size when ResizeToContents is used. |
| A low value will provide a less accurate but fast auto resize while a higher |
| value will provide a more accurate resize that however can be slow. |
| |
| The number \a precision specifies how many sections that should be consider |
| when calculating the preferred size. |
| |
| The default value is 1000 meaning that a horizontal column with auto-resize will look |
| at maximum 1000 rows on calculating when doing an auto resize. |
| |
| Special value 0 means that it will look at only the visible area. |
| Special value -1 will imply looking at all elements. |
| |
| This value is used in QTableView::sizeHintForColumn(), QTableView::sizeHintForRow() |
| and QTreeView::sizeHintForColumn(). Reimplementing these functions can make this |
| function not having an effect. |
| |
| \sa resizeContentsPrecision(), setSectionResizeMode(), resizeSections(), QTableView::sizeHintForColumn(), QTableView::sizeHintForRow(), QTreeView::sizeHintForColumn() |
| */ |
| |
| void QHeaderView::setResizeContentsPrecision(int precision) |
| { |
| Q_D(QHeaderView); |
| d->resizeContentsPrecision = precision; |
| } |
| |
| /*! |
| \since 5.2 |
| Returns how precise QHeaderView will calculate on ResizeToContents. |
| |
| \sa setResizeContentsPrecision(), setSectionResizeMode() |
| |
| */ |
| |
| int QHeaderView::resizeContentsPrecision() const |
| { |
| Q_D(const QHeaderView); |
| return d->resizeContentsPrecision; |
| } |
| |
| /*! |
| \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 |
| */ |
| |
| 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 \c false. |
| |
| \sa setSectionsClickable() |
| */ |
| |
| void QHeaderView::setSortIndicatorShown(bool show) |
| { |
| Q_D(QHeaderView); |
| if (d->sortIndicatorShown == show) |
| return; |
| |
| d->sortIndicatorShown = show; |
| |
| if (sortIndicatorSection() < 0 || sortIndicatorSection() > count()) |
| return; |
| |
| if (d->headerSectionResizeMode(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; |
| if (old == logicalIndex && order == d->sortIndicatorOrder) |
| return; |
| d->sortIndicatorSection = logicalIndex; |
| d->sortIndicatorOrder = order; |
| |
| if (logicalIndex >= d->sectionCount()) { |
| emit sortIndicatorChanged(logicalIndex, order); |
| return; // nothing to do |
| } |
| |
| if (old != logicalIndex |
| && ((logicalIndex >= 0 && sectionResizeMode(logicalIndex) == ResizeToContents) |
| || old >= d->sectionCount() || (old >= 0 && sectionResizeMode(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 setSectionResizeMode() |
| */ |
| bool QHeaderView::stretchLastSection() const |
| { |
| Q_D(const QHeaderView); |
| return d->stretchLastSection; |
| } |
| |
| void QHeaderView::setStretchLastSection(bool stretch) |
| { |
| Q_D(QHeaderView); |
| if (d->stretchLastSection == stretch) |
| return; |
| d->stretchLastSection = stretch; |
| if (d->state != QHeaderViewPrivate::NoState) |
| return; |
| if (stretch) { |
| d->setNewLastSection(d->lastVisibleVisualIndex()); |
| resizeSections(); |
| } else { |
| d->restoreSizeOnPrevLastSection(); |
| } |
| } |
| |
| /*! |
| \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 setSectionResizeMode() |
| */ |
| 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. |
| |
| By default, the value of this property is style dependent. |
| Thus, when the style changes, this property updates from it. |
| Calling setDefaultSectionSize() stops the updates, calling |
| resetDefaultSectionSize() will restore default behavior. |
| |
| \sa setSectionResizeMode(), minimumSectionSize |
| */ |
| int QHeaderView::defaultSectionSize() const |
| { |
| Q_D(const QHeaderView); |
| return d->defaultSectionSize; |
| } |
| |
| void QHeaderView::setDefaultSectionSize(int size) |
| { |
| Q_D(QHeaderView); |
| if (size < 0 || size > maxSizeSection) |
| return; |
| d->setDefaultSectionSize(size); |
| } |
| |
| void QHeaderView::resetDefaultSectionSize() |
| { |
| Q_D(QHeaderView); |
| if (d->customDefaultSectionSize) { |
| d->updateDefaultSectionSizeFromStyle(); |
| d->customDefaultSectionSize = false; |
| } |
| } |
| |
| /*! |
| \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 setSectionResizeMode(), defaultSectionSize |
| */ |
| int QHeaderView::minimumSectionSize() const |
| { |
| Q_D(const QHeaderView); |
| if (d->minimumSectionSize == -1) { |
| QSize strut = QApplication::globalStrut(); |
| int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this); |
| if (d->orientation == Qt::Horizontal) |
| return qMax(strut.width(), (fontMetrics().maxWidth() + margin)); |
| return qMax(strut.height(), (fontMetrics().height() + margin)); |
| } |
| return d->minimumSectionSize; |
| } |
| |
| void QHeaderView::setMinimumSectionSize(int size) |
| { |
| Q_D(QHeaderView); |
| if (size < -1 || size > maxSizeSection) |
| return; |
| // larger new min size - check current section sizes |
| const bool needSizeCheck = size > d->minimumSectionSize; |
| d->minimumSectionSize = size; |
| if (d->minimumSectionSize > maximumSectionSize()) |
| setMaximumSectionSize(size); |
| |
| if (needSizeCheck) { |
| if (d->hasAutoResizeSections()) { |
| d->doDelayedResizeSections(); |
| } else { |
| for (int visual = 0; visual < d->sectionCount(); ++visual) { |
| if (d->isVisualIndexHidden(visual)) |
| continue; |
| if (d->headerSectionSize(visual) < d->minimumSectionSize) |
| resizeSection(logicalIndex(visual), size); |
| } |
| } |
| } |
| |
| } |
| |
| /*! |
| \since 5.2 |
| \property QHeaderView::maximumSectionSize |
| \brief the maximum size of the header sections. |
| |
| The maximum section size is the largest section size allowed. |
| The default value for this property is 1048575, which is also the largest |
| possible size for a section. Setting maximum to -1 will reset the value to |
| the largest section size. |
| |
| With exception of stretch this property is honored by all \l{ResizeMode}{resize modes} |
| |
| \sa setSectionResizeMode(), defaultSectionSize |
| */ |
| int QHeaderView::maximumSectionSize() const |
| { |
| Q_D(const QHeaderView); |
| if (d->maximumSectionSize == -1) |
| return maxSizeSection; |
| return d->maximumSectionSize; |
| } |
| |
| void QHeaderView::setMaximumSectionSize(int size) |
| { |
| Q_D(QHeaderView); |
| if (size == -1) { |
| d->maximumSectionSize = maxSizeSection; |
| return; |
| } |
| if (size < 0 || size > maxSizeSection) |
| return; |
| if (minimumSectionSize() > size) |
| d->minimumSectionSize = size; |
| |
| // smaller new max size - check current section sizes |
| const bool needSizeCheck = size < d->maximumSectionSize; |
| d->maximumSectionSize = size; |
| |
| if (needSizeCheck) { |
| if (d->hasAutoResizeSections()) { |
| d->doDelayedResizeSections(); |
| } else { |
| for (int visual = 0; visual < d->sectionCount(); ++visual) { |
| if (d->isVisualIndexHidden(visual)) |
| continue; |
| if (d->headerSectionSize(visual) > d->maximumSectionSize) |
| resizeSection(logicalIndex(visual), 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 \c 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 \c 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 // QT_NO_DATASTREAM |
| |
| /*! |
| \reimp |
| */ |
| void QHeaderView::reset() |
| { |
| Q_D(QHeaderView); |
| 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(); |
| d->invalidateCachedSizeHint(); |
| } |
| |
| /*! |
| 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 root level |
| int oldCount = d->sectionCount(); |
| |
| d->invalidateCachedSizeHint(); |
| |
| if (d->state == QHeaderViewPrivate::ResizeSection) |
| d->preventCursorChangeInSetOffset = true; |
| |
| // add the new sections |
| int insertAt = logicalFirst; |
| int insertCount = logicalLast - logicalFirst + 1; |
| |
| bool lastSectionActualChange = false; |
| if (stretchLastSection()) { |
| |
| int visualIndexForStretch = d->lastSectionLogicalIdx; |
| if (d->lastSectionLogicalIdx >= 0 && d->lastSectionLogicalIdx < d->visualIndices.size()) |
| visualIndexForStretch = d->visualIndices[d->lastSectionLogicalIdx]; // We cannot call visualIndex since it executes executePostedLayout() |
| // and it is likely to bypass initializeSections() and we may end up here again. Doing the insert twice. |
| |
| if (d->lastSectionLogicalIdx < 0 || insertAt >= visualIndexForStretch) |
| lastSectionActualChange = true; |
| |
| if (d->lastSectionLogicalIdx >= logicalFirst) |
| d->lastSectionLogicalIdx += insertCount; // We do not want to emit resize before we have fixed the count |
| } |
| |
| QHeaderViewPrivate::SectionItem section(d->defaultSectionSize, d->globalResizeMode); |
| d->sectionStartposRecalc = true; |
| |
| if (d->sectionItems.isEmpty() || insertAt >= d->sectionItems.count()) { |
| int insertLength = d->defaultSectionSize * insertCount; |
| d->length += insertLength; |
| d->sectionItems.insert(d->sectionItems.count(), insertCount, section); // append |
| } else { |
| // separate them out into their own sections |
| int insertLength = d->defaultSectionSize * insertCount; |
| d->length += insertLength; |
| d->sectionItems.insert(insertAt, insertCount, section); |
| } |
| |
| // 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 hiddenSectionSize |
| QHash<int, int> newHiddenSectionSize; // from logical index to section size |
| for (QHash<int, int>::const_iterator it = d->hiddenSectionSize.cbegin(), |
| end = d->hiddenSectionSize.cend(); it != end; ++it) { |
| const int oldIndex = it.key(); |
| const int newIndex = (oldIndex < logicalFirst) ? oldIndex : oldIndex + insertCount; |
| newHiddenSectionSize[newIndex] = it.value(); |
| } |
| d->hiddenSectionSize.swap(newHiddenSectionSize); |
| |
| d->doDelayedResizeSections(); |
| emit sectionCountChanged(oldCount, count()); |
| |
| if (lastSectionActualChange) |
| d->maybeRestorePrevLastSectionAndStretchLast(); |
| |
| // 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; |
| } |
| |
| void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent, |
| int logicalFirst, int logicalLast) |
| { |
| Q_Q(QHeaderView); |
| if (parent != root) |
| return; // we only handle changes in the root level |
| if (qMin(logicalFirst, logicalLast) < 0 |
| || qMax(logicalLast, logicalFirst) >= sectionCount()) |
| return; |
| int oldCount = q->count(); |
| int changeCount = logicalLast - logicalFirst + 1; |
| |
| if (state == QHeaderViewPrivate::ResizeSection) |
| preventCursorChangeInSetOffset = true; |
| |
| updateHiddenSections(logicalFirst, logicalLast); |
| |
| if (visualIndices.isEmpty() && logicalIndices.isEmpty()) { |
| //Q_ASSERT(headerSectionCount() == sectionCount); |
| removeSectionsFromSectionItems(logicalFirst, logicalLast); |
| } else { |
| if (logicalFirst == logicalLast) { // Remove just one index. |
| int l = logicalFirst; |
| int visual = visualIndices.at(l); |
| Q_ASSERT(sectionCount() == logicalIndices.count()); |
| for (int v = 0; v < sectionCount(); ++v) { |
| 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); |
| removeSectionsFromSectionItems(visual, visual); |
| } else { |
| sectionStartposRecalc = true; // We will need to recalc positions after removing items |
| for (int u = 0; u < sectionItems.count(); ++u) // Store section info |
| sectionItems.at(u).tmpLogIdx = logicalIndices.at(u); |
| for (int v = sectionItems.count() - 1; v >= 0; --v) { // Remove the sections |
| if (logicalFirst <= sectionItems.at(v).tmpLogIdx && sectionItems.at(v).tmpLogIdx <= logicalLast) |
| removeSectionsFromSectionItems(v, v); |
| } |
| visualIndices.resize(sectionItems.count()); |
| logicalIndices.resize(sectionItems.count()); |
| int* visual_data = visualIndices.data(); |
| int* logical_data = logicalIndices.data(); |
| for (int w = 0; w < sectionItems.count(); ++w) { // Restore visual and logical indexes |
| int logindex = sectionItems.at(w).tmpLogIdx; |
| if (logindex > logicalFirst) |
| logindex -= changeCount; |
| visual_data[logindex] = w; |
| logical_data[w] = logindex; |
| } |
| } |
| // ### handle sectionSelection (sectionHidden is handled by updateHiddenSections) |
| } |
| |
| // 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()); |
| |
| if (q->stretchLastSection()) { |
| const bool lastSectionRemoved = lastSectionLogicalIdx >= logicalFirst && lastSectionLogicalIdx <= logicalLast; |
| if (lastSectionRemoved) |
| setNewLastSection(lastVisibleVisualIndex()); |
| else |
| lastSectionLogicalIdx = logicalIndex(lastVisibleVisualIndex()); // Just update the last log index. |
| doDelayedResizeSections(); |
| } |
| |
| viewport->update(); |
| } |
| |
| void QHeaderViewPrivate::_q_sectionsAboutToBeMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination) |
| { |
| if (sourceParent != root || destinationParent != root) |
| return; // we only handle changes in the root level |
| Q_UNUSED(logicalStart); |
| Q_UNUSED(logicalEnd); |
| Q_UNUSED(logicalDestination); |
| _q_sectionsAboutToBeChanged(); |
| } |
| |
| void QHeaderViewPrivate::_q_sectionsMoved(const QModelIndex &sourceParent, int logicalStart, int logicalEnd, const QModelIndex &destinationParent, int logicalDestination) |
| { |
| if (sourceParent != root || destinationParent != root) |
| return; // we only handle changes in the root level |
| Q_UNUSED(logicalStart); |
| Q_UNUSED(logicalEnd); |
| Q_UNUSED(logicalDestination); |
| _q_sectionsChanged(); |
| } |
| |
| void QHeaderViewPrivate::_q_sectionsAboutToBeChanged(const QList<QPersistentModelIndex> &, |
| QAbstractItemModel::LayoutChangeHint hint) |
| { |
| if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) || |
| (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical)) |
| return; |
| |
| //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; |
| |
| layoutChangePersistentSections.clear(); |
| layoutChangePersistentSections.reserve(std::min(10, sectionItems.count())); |
| // after layoutChanged another section can be last stretched section |
| if (stretchLastSection && lastSectionLogicalIdx >= 0 && lastSectionLogicalIdx < sectionItems.count()) { |
| const int visual = visualIndex(lastSectionLogicalIdx); |
| if (visual >= 0 && visual < sectionItems.size()) { |
| auto &itemRef = sectionItems[visual]; |
| if (itemRef.size != lastSectionSize) { |
| length += lastSectionSize - itemRef.size; |
| itemRef.size = lastSectionSize; |
| } |
| } |
| } |
| for (int i = 0; i < sectionItems.size(); ++i) { |
| auto s = sectionItems.at(i); |
| // only add if the section is not default and not visually moved |
| if (s.size == defaultSectionSize && !s.isHidden && s.resizeMode == globalResizeMode) |
| continue; |
| |
| const int logical = logicalIndex(i); |
| if (s.isHidden) |
| s.size = hiddenSectionSize.value(logical); |
| |
| // ### note that we are using column or row 0 |
| layoutChangePersistentSections.append({orientation == Qt::Horizontal |
| ? model->index(0, logical, root) |
| : model->index(logical, 0, root), |
| s}); |
| } |
| } |
| |
| void QHeaderViewPrivate::_q_sectionsChanged(const QList<QPersistentModelIndex> &, |
| QAbstractItemModel::LayoutChangeHint hint) |
| { |
| if ((hint == QAbstractItemModel::VerticalSortHint && orientation == Qt::Horizontal) || |
| (hint == QAbstractItemModel::HorizontalSortHint && orientation == Qt::Vertical)) |
| return; |
| |
| Q_Q(QHeaderView); |
| viewport->update(); |
| |
| const auto oldPersistentSections = layoutChangePersistentSections; |
| layoutChangePersistentSections.clear(); |
| |
| const int newCount = modelSectionCount(); |
| const int oldCount = sectionItems.size(); |
| if (newCount == 0) { |
| clear(); |
| if (oldCount != 0) |
| emit q->sectionCountChanged(oldCount, 0); |
| return; |
| } |
| |
| bool hasPersistantIndexes = false; |
| for (const auto &item : oldPersistentSections) { |
| if (item.index.isValid()) { |
| hasPersistantIndexes = true; |
| break; |
| } |
| } |
| |
| // Though far from perfect we here try to retain earlier/existing behavior |
| // ### See QHeaderViewPrivate::_q_layoutAboutToBeChanged() |
| // When we don't have valid hasPersistantIndexes it can be due to |
| // - all sections are default sections |
| // - the row/column 0 which is used for persistent indexes is gone |
| // - all non-default sections were removed |
| // case one is trivial, in case two we assume nothing else changed (it's the best |
| // guess we can do - everything else can not be handled correctly for now) |
| // case three can not be handled correctly with layoutChanged - removeSections |
| // should be used instead for this |
| if (!hasPersistantIndexes) { |
| if (oldCount != newCount) |
| q->initializeSections(); |
| return; |
| } |
| |
| // adjust section size |
| if (newCount != oldCount) { |
| const int min = qBound(0, oldCount, newCount - 1); |
| q->initializeSections(min, newCount - 1); |
| } |
| // reset sections |
| sectionItems.fill(SectionItem(defaultSectionSize, globalResizeMode), newCount); |
| |
| // all hidden sections are in oldPersistentSections |
| hiddenSectionSize.clear(); |
| |
| for (const auto &item : oldPersistentSections) { |
| const auto &index = item.index; |
| if (!index.isValid()) |
| continue; |
| |
| const int newLogicalIndex = (orientation == Qt::Horizontal |
| ? index.column() |
| : index.row()); |
| // the new visualIndices are already adjusted / reset by initializeSections() |
| const int newVisualIndex = visualIndex(newLogicalIndex); |
| if (newVisualIndex < sectionItems.count()) { |
| auto &newSection = sectionItems[newVisualIndex]; |
| newSection = item.section; |
| |
| if (newSection.isHidden) { |
| // otherwise setSectionHidden will return without doing anything |
| newSection.isHidden = false; |
| q->setSectionHidden(newLogicalIndex, true); |
| } |
| } |
| } |
| |
| recalcSectionStartPos(); |
| length = headerLength(); |
| |
| if (stretchLastSection) { |
| // force rebuild of stretched section later on |
| lastSectionLogicalIdx = -1; |
| maybeRestorePrevLastSectionAndStretchLast(); |
| } |
| } |
| |
| /*! |
| \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->maybeRestorePrevLastSectionAndStretchLast(); |
| |
| // make sure we update the hidden sections |
| // simulate remove from newCount to oldCount |
| if (newCount < oldCount) |
| d->updateHiddenSections(newCount, oldCount); |
| } |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QHeaderView::initializeSections(int start, int end) |
| { |
| Q_D(QHeaderView); |
| |
| Q_ASSERT(start >= 0); |
| Q_ASSERT(end >= 0); |
| |
| d->invalidateCachedSizeHint(); |
| int oldCount = d->sectionCount(); |
| |
| if (end + 1 < d->sectionCount()) { |
| int newCount = end + 1; |
| d->removeSectionsFromSectionItems(newCount, d->sectionCount() - 1); |
| if (!d->hiddenSectionSize.isEmpty()) { |
| if (oldCount - 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 newSectionCount = end + 1; |
| |
| if (!d->logicalIndices.isEmpty()) { |
| if (oldCount <= newSectionCount) { |
| d->logicalIndices.resize(newSectionCount); |
| d->visualIndices.resize(newSectionCount); |
| for (int i = oldCount; i < newSectionCount; ++i) { |
| d->logicalIndices[i] = i; |
| d->visualIndices[i] = i; |
| } |
| } else { |
| int j = 0; |
| for (int i = 0; i < oldCount; ++i) { |
| int v = d->logicalIndices.at(i); |
| if (v < newSectionCount) { |
| d->logicalIndices[j] = v; |
| d->visualIndices[v] = j; |
| j++; |
| } |
| } |
| d->logicalIndices.resize(newSectionCount); |
| d->visualIndices.resize(newSectionCount); |
| } |
| } |
| |
| if (d->globalResizeMode == Stretch) |
| d->stretchSections = newSectionCount; |
| else if (d->globalResizeMode == ResizeToContents) |
| d->contentsSections = newSectionCount; |
| |
| if (newSectionCount > oldCount) |
| d->createSectionItems(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->viewport->update(QRect(sectionViewportPosition(old.column()), 0, |
| sectionSize(old.column()), d->viewport->height())); |
| if (current.isValid() && current.parent() == d->root) |
| d->viewport->update(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->viewport->update(QRect(0, sectionViewportPosition(old.row()), |
| d->viewport->width(), sectionSize(old.row()))); |
| if (current.isValid() && current.parent() == d->root) |
| d->viewport->update(QRect(0, sectionViewportPosition(current.row()), |
| d->viewport->width(), sectionSize(current.row()))); |
| } |
| } |
| |
| |
| /*! |
| \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: { |
| QTimerEvent *te = static_cast<QTimerEvent*>(e); |
| if (te->timerId() == d->delayedResize.timerId()) { |
| d->delayedResize.stop(); |
| resizeSections(); |
| } |
| break; } |
| case QEvent::StyleChange: |
| if (!d->customDefaultSectionSize) |
| d->updateDefaultSectionSizeFromStyle(); |
| 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; |
| const int width = d->viewport->width(); |
| const int height = d->viewport->height(); |
| const int rtlHorizontalOffset = d->reverse() ? 1 : 0; |
| for (int i = start; i <= end; ++i) { |
| if (d->isVisualIndexHidden(i)) |
| continue; |
| painter.save(); |
| const int logical = logicalIndex(i); |
| if (d->orientation == Qt::Horizontal) { |
| currentSectionRect.setRect(sectionViewportPosition(logical) + rtlHorizontalOffset, |
| 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() && variant.canConvert<QFont>()) { |
| 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 sections |
| for (int a = 0, i = 0; i < d->sectionItems.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->sectionItems.at(i).size, 4, color); |
| else |
| painter.fillRect(0, a - d->offset, 4, d->sectionItems.at(i).size, color); |
| a += d->sectionItems.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); |
| |
| bool acceptMoveSection = d->movableSections; |
| if (acceptMoveSection && d->pressed == 0 && !d->allowUserMoveOfSection0) |
| acceptMoveSection = false; // Do not allow moving the tree nod |
| |
| if (acceptMoveSection) { |
| 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 (sectionResizeMode(handle) == Interactive) { |
| d->originalSize = sectionSize(handle); |
| d->state = QHeaderViewPrivate::ResizeSection; |
| d->section = handle; |
| d->preventCursorChangeInSetOffset = false; |
| } |
| |
| 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 && d->state != QHeaderViewPrivate::SelectSections) |
| return; |
| if (e->buttons() == Qt::NoButton) { |
| // Under Cocoa, when the mouse button is released, may include an extra |
| // simulated mouse moved event. The state of the buttons when this event |
| // is generated is already "no button" and the code below gets executed |
| // just before the mouseReleaseEvent and resets the state. This prevents |
| // column dragging from working. So this code is disabled under Cocoa. |
| 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; |
| int newsize = qBound(minimumSectionSize(), d->originalSize + delta, maximumSectionSize()); |
| resizeSection(d->section, newsize); |
| } |
| d->lastPos = pos; |
| return; |
| } |
| case QHeaderViewPrivate::MoveSection: { |
| if (d->shouldAutoScroll(e->pos())) |
| d->startAutoScroll(); |
| if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance() |
| #if QT_CONFIG(label) |
| || !d->sectionIndicator->isHidden() |
| #endif |
| ) { |
| int visual = visualIndexAt(pos); |
| if (visual == -1) |
| return; |
| if (visual == 0 && logicalIndex(0) == 0 && !d->allowUserMoveOfSection0) |
| return; |
| |
| int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2; |
| int moving = visualIndex(d->section); |
| if (visual < moving) { |
| if (pos < posThreshold) |
| d->target = d->logicalIndex(visual); |
| else |
| d->target = d->logicalIndex(visual + 1); |
| } else if (visual > moving) { |
| if (pos > posThreshold) |
| d->target = d->logicalIndex(visual); |
| else |
| d->target = d->logicalIndex(visual - 1); |
| } else { |
| d->target = d->section; |
| } |
| d->updateSectionIndicator(d->section, pos); |
| } |
| return; |
| } |
| case QHeaderViewPrivate::SelectSections: { |
| int logical = logicalIndexAt(qMax(-d->offset, pos)); |
| if (logical == -1 && pos > 0) |
| logical = logicalIndex(d->lastVisibleVisualIndex()); |
| 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 && (sectionResizeMode(handle) == Interactive)) { |
| if (!hasCursor) |
| setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor); |
| } else { |
| if (hasCursor) |
| unsetCursor(); |
| #ifndef QT_NO_STATUSTIP |
| int logical = logicalIndexAt(pos); |
| QString statusTip; |
| if (logical != -1) |
| statusTip = d->model->headerData(logical, d->orientation, Qt::StatusTipRole).toString(); |
| if (d->shouldClearStatusTip || !statusTip.isEmpty()) { |
| QStatusTipEvent tip(statusTip); |
| QCoreApplication::sendEvent(d->parent ? d->parent : this, &tip); |
| d->shouldClearStatusTip = !statusTip.isEmpty(); |
| } |
| #endif // !QT_NO_STATUSTIP |
| } |
| #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 (true |
| #if QT_CONFIG(label) |
| && !d->sectionIndicator->isHidden() |
| #endif |
| ) { // 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 |
| Q_FALLTHROUGH(); |
| case QHeaderViewPrivate::SelectSections: |
| if (!d->clickableSections) { |
| int section = logicalIndexAt(pos); |
| updateSection(section); |
| } |
| Q_FALLTHROUGH(); |
| case QHeaderViewPrivate::NoState: |
| if (d->clickableSections) { |
| int section = logicalIndexAt(pos); |
| if (section != -1 && section == d->pressed) { |
| d->flipSortIndicator(section); |
| emit sectionClicked(section); |
| } |
| 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 && sectionResizeMode(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 && sectionResizeMode(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 |
| #if QT_CONFIG(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_CONFIG(whatsthis) |
| #if QT_CONFIG(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_CONFIG(statustip) |
| case QEvent::Resize: |
| case QEvent::FontChange: |
| case QEvent::StyleChange: |
| d->invalidateCachedSizeHint(); |
| Q_FALLTHROUGH(); |
| case QEvent::Hide: |
| case QEvent::Show: { |
| QAbstractScrollArea *parent = qobject_cast<QAbstractScrollArea *>(parentWidget()); |
| if (parent && parent->isVisible()) // Only resize if we have a visible parent |
| resizeSections(); |
| emit geometriesChanged(); |
| break;} |
| case QEvent::ContextMenu: { |
| d->state = QHeaderViewPrivate::NoState; |
| d->pressed = d->section = d->target = -1; |
| d->updateSectionIndicator(d->section, -1); |
| break; } |
| case QEvent::Wheel: { |
| QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget()); |
| if (asa) |
| return QCoreApplication::sendEvent(asa->viewport(), e); |
| break; } |
| 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(); |
| |
| int margin = 2 * style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this); |
| |
| const Qt::Alignment headerArrowAlignment = static_cast<Qt::Alignment>(style()->styleHint(QStyle::SH_Header_ArrowAlignment, nullptr, this)); |
| const bool isHeaderArrowOnTheSide = headerArrowAlignment & Qt::AlignVCenter; |
| if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex && isHeaderArrowOnTheSide) |
| margin += style()->pixelMetric(QStyle::PM_HeaderMarkSize, nullptr, this); |
| |
| const 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); |
| if (!opt.icon.isNull()) // see CT_HeaderSection |
| margin += style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this) + |
| style()->pixelMetric(QStyle::PM_HeaderMargin, nullptr, this); |
| |
| if (d->textElideMode != Qt::ElideNone) { |
| const QRect textRect = style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this); |
| opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode, textRect.width() - margin); |
| } |
| |
| QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation, |
| Qt::ForegroundRole); |
| if (foregroundBrush.canConvert<QBrush>()) |
| opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush)); |
| |
| QPointF oldBO = painter->brushOrigin(); |
| QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation, |
| Qt::BackgroundRole); |
| if (backgroundBrush.canConvert<QBrush>()) { |
| 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); |
| bool first = d->isFirstVisibleSection(visual); |
| bool last = d->isLastVisibleSection(visual); |
| if (first && last) |
| opt.position = QStyleOptionHeader::OnlyOneSection; |
| else if (first) |
| opt.position = d->reverse() ? QStyleOptionHeader::End : QStyleOptionHeader::Beginning; |
| else if (last) |
| opt.position = d->reverse() ? QStyleOptionHeader::Beginning : 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); |
| |
| ensurePolished(); |
| |
| // 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() && var.canConvert<QFont>()) |
| 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); |
| if (isSortIndicatorShown()) |
| opt.sortIndicator = QStyleOptionHeader::SortDown; |
| return style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this); |
| } |
| |
| /*! |
| 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()) |
| d->doDelayedResizeSections(); |
| } |
| |
| /*! |
| \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, const QVector<int> &roles) |
| { |
| Q_D(QHeaderView); |
| if (!roles.isEmpty()) { |
| const auto doesRoleAffectSize = [](int role) -> bool { |
| switch (role) { |
| case Qt::DisplayRole: |
| case Qt::DecorationRole: |
| case Qt::SizeHintRole: |
| case Qt::FontRole: |
| return true; |
| default: |
| // who knows what a subclass or custom style might do |
| return role >= Qt::UserRole; |
| } |
| }; |
| if (std::none_of(roles.begin(), roles.end(), doesRoleAffectSize)) |
| return; |
| } |
| 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 = (sectionResizeMode(i) == ResizeToContents); |
| 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 logicalLeft = max; |
| int logicalRight = 0; |
| |
| if (d->visualIndices.empty()) { |
| // If no reordered sections, skip redundant visual-to-logical transformations |
| for (const auto &r : selection) { |
| if (r.parent().isValid() || !r.isValid()) |
| continue; // we only know about toplevel items and we don't want invalid ranges |
| if (r.left() < logicalLeft) |
| logicalLeft = r.left(); |
| if (r.right() > logicalRight) |
| logicalRight = r.right(); |
| } |
| } else { |
| int left = max; |
| int right = 0; |
| for (const auto &r : selection) { |
| if (r.parent().isValid() || !r.isValid()) |
| continue; // we only know about toplevel items and we don't want invalid ranges |
| for (int k = r.left(); k <= r.right(); ++k) { |
| int visual = visualIndex(k); |
| if (visual == -1) // in some cases users may change the selections |
| continue; // before we have a chance to do the layout |
| if (visual < left) |
| left = visual; |
| if (visual > right) |
| right = visual; |
| } |
| } |
| logicalLeft = logicalIndex(left); |
| 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 logicalTop = max; |
| int logicalBottom = 0; |
| |
| if (d->visualIndices.empty()) { |
| // If no reordered sections, skip redundant visual-to-logical transformations |
| for (const auto &r : selection) { |
| if (r.parent().isValid() || !r.isValid()) |
| continue; // we only know about toplevel items and we don't want invalid ranges |
| if (r.top() < logicalTop) |
| logicalTop = r.top(); |
| if (r.bottom() > logicalBottom) |
| logicalBottom = r.bottom(); |
| } |
| } else { |
| int top = max; |
| int bottom = 0; |
| |
| for (const auto &r : selection) { |
| if (r.parent().isValid() || !r.isValid()) |
| continue; // we only know about toplevel items and we don't want invalid ranges |
| for (int k = r.top(); k <= r.bottom(); ++k) { |
| int visual = visualIndex(k); |
| if (visual == -1) // in some cases users may change the selections |
| continue; // before we have a chance to do the layout |
| if (visual < top) |
| top = visual; |
| if (visual > bottom) |
| bottom = visual; |
| } |
| } |
| |
| logicalTop = logicalIndex(top); |
| logicalBottom = logicalIndex(bottom); |
| } |
| |
| if (logicalTop < 0 || logicalTop >= count() || |
| logicalBottom < 0 || logicalBottom >= count()) |
| return QRegion(); |
| |
| 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, nullptr, 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 QT_CONFIG(label) |
| if (!sectionIndicator) { |
| sectionIndicator = new QLabel(viewport); |
| } |
| #endif |
| |
| int w, h; |
| int p = q->sectionViewportPosition(section); |
| if (orientation == Qt::Horizontal) { |
| w = q->sectionSize(section); |
| h = viewport->height(); |
| } else { |
| w = viewport->width(); |
| h = q->sectionSize(section); |
| } |
| #if QT_CONFIG(label) |
| sectionIndicator->resize(w, h); |
| #endif |
| |
| const qreal pixmapDevicePixelRatio = q->devicePixelRatioF(); |
| QPixmap pm(QSize(w, h) * pixmapDevicePixelRatio); |
| pm.setDevicePixelRatio(pixmapDevicePixelRatio); |
| pm.fill(QColor(0, 0, 0, 45)); |
| QRect rect(0, 0, w, h); |
| |
| QPainter painter(&pm); |
| const QVariant variant = model->headerData(section, orientation, |
| Qt::FontRole); |
| if (variant.isValid() && variant.canConvert<QFont>()) { |
| const QFont sectionFont = qvariant_cast<QFont>(variant); |
| painter.setFont(sectionFont); |
| } else { |
| painter.setFont(q->font()); |
| } |
| |
| painter.setOpacity(0.75); |
| q->paintSection(&painter, rect, section); |
| painter.end(); |
| |
| #if QT_CONFIG(label) |
| sectionIndicator->setPixmap(pm); |
| #endif |
| sectionIndicatorOffset = position - qMax(p, 0); |
| } |
| |
| void QHeaderViewPrivate::updateSectionIndicator(int section, int position) |
| { |
| #if QT_CONFIG(label) |
| 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(); |
| #endif |
| } |
| |
| /*! |
| 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; |
| } |
| |
| bool QHeaderViewPrivate::isFirstVisibleSection(int section) const |
| { |
| if (sectionStartposRecalc) |
| recalcSectionStartPos(); |
| const SectionItem &item = sectionItems.at(section); |
| return item.size > 0 && item.calculated_startpos == 0; |
| } |
| |
| bool QHeaderViewPrivate::isLastVisibleSection(int section) const |
| { |
| if (sectionStartposRecalc) |
| recalcSectionStartPos(); |
| const SectionItem &item = sectionItems.at(section); |
| return item.size > 0 && item.calculatedEndPos() == length; |
| } |
| |
| /*! |
| \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; |
| } |
| |
| void QHeaderViewPrivate::restoreSizeOnPrevLastSection() |
| { |
| Q_Q(QHeaderView); |
| if (lastSectionLogicalIdx < 0) |
| return; |
| int resizeLogIdx = lastSectionLogicalIdx; |
| lastSectionLogicalIdx = -1; // We do not want resize to catch it as the last section. |
| q->resizeSection(resizeLogIdx, lastSectionSize); |
| } |
| |
| void QHeaderViewPrivate::setNewLastSection(int visualIndexForLastSection) |
| { |
| Q_Q(QHeaderView); |
| lastSectionSize = -1; |
| lastSectionLogicalIdx = q->logicalIndex(visualIndexForLastSection); |
| lastSectionSize = headerSectionSize(visualIndexForLastSection); // pick size directly since ... |
| // q->sectionSize(lastSectionLogicalIdx) may do delayed resize and stretch it before we get the value. |
| } |
| |
| void QHeaderViewPrivate::maybeRestorePrevLastSectionAndStretchLast() |
| { |
| Q_Q(const QHeaderView); |
| if (!q->stretchLastSection()) |
| return; |
| |
| int nowLastVisualSection = lastVisibleVisualIndex(); |
| if (lastSectionLogicalIdx == q->logicalIndex(nowLastVisualSection)) |
| return; |
| |
| // restore old last section. |
| restoreSizeOnPrevLastSection(); |
| setNewLastSection(nowLastVisualSection); |
| doDelayedResizeSections(); // Do stretch of last section soon (but not now). |
| } |
| |
| |
| /*! |
| \internal |
| Go through and resize all of the sections applying stretchLastSection, |
| manual 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); |
| //stop the timer in case it is delayed |
| delayedResize.stop(); |
| |
| executePostedLayout(); |
| if (sectionCount() == 0) |
| return; |
| |
| if (resizeRecursionBlock) |
| return; |
| resizeRecursionBlock = true; |
| |
| invalidateCachedSizeHint(); |
| const int lastSectionVisualIdx = q->visualIndex(lastSectionLogicalIdx); |
| |
| // find stretchLastSection if we have it |
| int stretchSection = -1; |
| if (stretchLastSection && !useGlobalMode) |
| stretchSection = lastSectionVisualIdx; |
| |
| // count up the number of stretched sections and how much space left for them |
| int lengthToStretch = (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 : headerSectionResizeMode(i)); |
| |
| if (resizeMode == QHeaderView::Stretch) { |
| ++numberOfStretchedSections; |
| section_sizes.append(headerSectionSize(i)); |
| continue; |
| } |
| |
| // because it isn't stretch, determine its width and remove that from lengthToStretch |
| int sectionSize = 0; |
| if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) { |
| sectionSize = qBound(q->minimumSectionSize(), headerSectionSize(i), q->maximumSectionSize()); |
| } else { // resizeMode == QHeaderView::ResizeToContents |
| int logicalIndex = q->logicalIndex(i); |
| sectionSize = qMax(viewSectionSizeHint(logicalIndex), |
| q->sectionSizeHint(logicalIndex)); |
| } |
| sectionSize = qBound(q->minimumSectionSize(), |
| sectionSize, |
| q->maximumSectionSize()); |
| |
| section_sizes.append(sectionSize); |
| lengthToStretch -= sectionSize; |
| } |
| |
| // calculate the new length for all of the stretched sections |
| int stretchSectionLength = -1; |
| int pixelReminder = 0; |
| if (numberOfStretchedSections > 0 && lengthToStretch > 0) { // we have room to stretch in |
| int hintLengthForEveryStretchedSection = lengthToStretch / numberOfStretchedSections; |
| stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize()); |
| pixelReminder = lengthToStretch % numberOfStretchedSections; |
| } |
| |
| // ### The code below would be nicer if it was cleaned up a bit (since spans has been replaced with items) |
| int spanStartSection = 0; |
| int previousSectionLength = 0; |
| |
| 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 |
| : newSectionResizeMode); |
| if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) { |
| if (i == lastSectionVisualIdx) |
| newSectionLength = qMax(stretchSectionLength, lastSectionSize); |
| else |
| newSectionLength = stretchSectionLength; |
| if (pixelReminder > 0) { |
| newSectionLength += 1; |
| --pixelReminder; |
| } |
| section_sizes.removeFirst(); |
| } else { |
| newSectionLength = section_sizes.takeFirst(); |
| } |
| } |
| |
| //Q_ASSERT(newSectionLength > 0); |
| if ((previousSectionResizeMode != newSectionResizeMode |
| || previousSectionLength != newSectionLength) && i > 0) { |
| int spanLength = (i - spanStartSection) * previousSectionLength; |
| createSectionItems(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; |
| } |
| |
| createSectionItems(spanStartSection, sectionCount() - 1, |
| (sectionCount() - spanStartSection) * previousSectionLength, |
| previousSectionResizeMode); |
| //Q_ASSERT(headerLength() == length); |
| resizeRecursionBlock = false; |
| viewport->update(); |
| } |
| |
| void QHeaderViewPrivate::createSectionItems(int start, int end, int size, QHeaderView::ResizeMode mode) |
| { |
| int sizePerSection = size / (end - start + 1); |
| if (end >= sectionItems.count()) { |
| sectionItems.resize(end + 1); |
| sectionStartposRecalc = true; |
| } |
| SectionItem *sectiondata = sectionItems.data(); |
| for (int i = start; i <= end; ++i) { |
| length += (sizePerSection - sectiondata[i].size); |
| sectionStartposRecalc |= (sectiondata[i].size != sizePerSection); |
| sectiondata[i].size = sizePerSection; |
| sectiondata[i].resizeMode = mode; |
| } |
| } |
| |
| void QHeaderViewPrivate::removeSectionsFromSectionItems(int start, int end) |
| { |
| // remove sections |
| sectionStartposRecalc |= (end != sectionItems.count() - 1); |
| int removedlength = 0; |
| for (int u = start; u <= end; ++u) |
| removedlength += sectionItems.at(u).size; |
| length -= removedlength; |
| sectionItems.remove(start, end - start + 1); |
| } |
| |
| void QHeaderViewPrivate::clear() |
| { |
| if (state != NoClear) { |
| length = 0; |
| visualIndices.clear(); |
| logicalIndices.clear(); |
| sectionSelected.clear(); |
| hiddenSectionSize.clear(); |
| sectionItems.clear(); |
| lastSectionLogicalIdx = -1; |
| invalidateCachedSizeHint(); |
| } |
| } |
| |
| void QHeaderViewPrivate::flipSortIndicator(int section) |
| { |
| Q_Q(QHeaderView); |
| Qt::SortOrder sortOrder; |
| if (sortIndicatorSection == section) { |
| sortOrder = (sortIndicatorOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder; |
| } else { |
| const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole); |
| if (value.canConvert(QMetaType::Int)) |
| sortOrder = static_cast<Qt::SortOrder>(value.toInt()); |
| else |
| sortOrder = Qt::AscendingOrder; |
| } |
| q->setSortIndicator(section, sortOrder); |
| } |
| |
| 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; |
| resizeSectionItem(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) |
| resizeSectionItem(visual, oldSize, newSize); |
| } |
| |
| // cascade the section size change |
| for (int i = visual + 1; i < sectionCount(); ++i) { |
| if (isVisualIndexHidden(i)) |
| continue; |
| 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; |
| resizeSectionItem(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; |
| resizeSectionItem(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 |
| resizeSectionItem(visual, oldSize, qMax(newSize, minimumSize)); |
| |
| // cascade the section size change |
| if (delta < 0 && newSize < minimumSize) { |
| for (int i = visual - 1; i >= 0; --i) { |
| if (isVisualIndexHidden(i)) |
| continue; |
| if (!sectionIsCascadable(i)) |
| continue; |
| int sectionSize = headerSectionSize(i); |
| if (sectionSize <= minimumSize) |
| continue; |
| resizeSectionItem(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 (isVisualIndexHidden(i)) |
| continue; |
| if (!sectionIsCascadable(i)) |
| continue; |
| int currentSectionSize = headerSectionSize(i); |
| int newSectionSize = qMax(currentSectionSize - delta, minimumSize); |
| resizeSectionItem(i, currentSectionSize, newSectionSize); |
| break; |
| } |
| } |
| } |
| |
| if (hasAutoResizeSections()) |
| doDelayedResizeSections(); |
| |
| viewport->update(); |
| } |
| |
| void QHeaderViewPrivate::setDefaultSectionSize(int size) |
| { |
| Q_Q(QHeaderView); |
| size = qBound(q->minimumSectionSize(), size, q->maximumSectionSize()); |
| executePostedLayout(); |
| invalidateCachedSizeHint(); |
| defaultSectionSize = size; |
| customDefaultSectionSize = true; |
| if (state == QHeaderViewPrivate::ResizeSection) |
| preventCursorChangeInSetOffset = true; |
| for (int i = 0; i < sectionItems.count(); ++i) { |
| QHeaderViewPrivate::SectionItem §ion = sectionItems[i]; |
| if (hiddenSectionSize.isEmpty() || !isVisualIndexHidden(i)) { // resize on not hidden. |
| const int newSize = size; |
| if (newSize != section.size) { |
| length += newSize - section.size; //the whole length is changed |
| const int oldSectionSize = section.sectionSize(); |
| section.size = size; |
| emit q->sectionResized(logicalIndex(i), oldSectionSize, size); |
| } |
| } |
| } |
| sectionStartposRecalc = true; |
| if (hasAutoResizeSections()) |
| doDelayedResizeSections(); |
| viewport->update(); |
| } |
| |
| void QHeaderViewPrivate::updateDefaultSectionSizeFromStyle() |
| { |
| Q_Q(QHeaderView); |
| if (orientation == Qt::Horizontal) { |
| defaultSectionSize = q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeHorizontal, nullptr, q); |
| } else { |
| defaultSectionSize = qMax(q->minimumSectionSize(), |
| q->style()->pixelMetric(QStyle::PM_HeaderDefaultSectionSizeVertical, nullptr, q)); |
| } |
| } |
| |
| void QHeaderViewPrivate::recalcSectionStartPos() const // linear (but fast) |
| { |
| int pixelpos = 0; |
| for (const SectionItem &i : sectionItems) { |
| i.calculated_startpos = pixelpos; // write into const mutable |
| pixelpos += i.size; |
| } |
| sectionStartposRecalc = false; |
| } |
| |
| void QHeaderViewPrivate::resizeSectionItem(int visualIndex, int oldSize, int newSize) |
| { |
| Q_Q(QHeaderView); |
| QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex); |
| createSectionItems(visualIndex, visualIndex, newSize, mode); |
| emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize); |
| } |
| |
| int QHeaderViewPrivate::headerSectionSize(int visual) const |
| { |
| if (visual < sectionCount() && visual >= 0) |
| return sectionItems.at(visual).sectionSize(); |
| return -1; |
| } |
| |
| int QHeaderViewPrivate::headerSectionPosition(int visual) const |
| { |
| if (visual < sectionCount() && visual >= 0) { |
| if (sectionStartposRecalc) |
| recalcSectionStartPos(); |
| return sectionItems.at(visual).calculated_startpos; |
| } |
| return -1; |
| } |
| |
| int QHeaderViewPrivate::headerVisualIndexAt(int position) const |
| { |
| if (sectionStartposRecalc) |
| recalcSectionStartPos(); |
| int startidx = 0; |
| int endidx = sectionItems.count() - 1; |
| while (startidx <= endidx) { |
| int middle = (endidx + startidx) / 2; |
| if (sectionItems.at(middle).calculated_startpos > position) { |
| endidx = middle - 1; |
| } else { |
| if (sectionItems.at(middle).calculatedEndPos() <= position) |
| startidx = middle + 1; |
| else // we found it. |
| return middle; |
| } |
| } |
| return -1; |
| } |
| |
| void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode) |
| { |
| int size = headerSectionSize(visual); |
| createSectionItems(visual, visual, size, mode); |
| } |
| |
| QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const |
| { |
| if (visual < 0 || visual >= sectionItems.count()) |
| return globalResizeMode; |
| return static_cast<QHeaderView::ResizeMode>(sectionItems.at(visual).resizeMode); |
| } |
| |
| void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode) |
| { |
| globalResizeMode = mode; |
| for (int i = 0; i < sectionItems.count(); ++i) |
| sectionItems[i].resizeMode = mode; |
| } |
| |
| int QHeaderViewPrivate::viewSectionSizeHint(int logical) const |
| { |
| if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) { |
| return (orientation == Qt::Horizontal |
| ? view->sizeHintForColumn(logical) |
| : view->sizeHintForRow(logical)); |
| } |
| return 0; |
| } |
| |
| int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const |
| { |
| if (!hiddenSectionSize.isEmpty()) { |
| int adjustedVisualIndex = visualIndex; |
| int currentVisualIndex = 0; |
| for (int i = 0; i < sectionItems.count(); ++i) { |
| if (isVisualIndexHidden(i)) |
| ++adjustedVisualIndex; |
| else |
| ++currentVisualIndex; |
| if (currentVisualIndex >= visualIndex) |
| break; |
| } |
| visualIndex = adjustedVisualIndex; |
| } |
| return visualIndex; |
| } |
| |
| void QHeaderViewPrivate::setScrollOffset(const QScrollBar *scrollBar, QAbstractItemView::ScrollMode scrollMode) |
| { |
| Q_Q(QHeaderView); |
| if (scrollMode == QAbstractItemView::ScrollPerItem) { |
| if (scrollBar->maximum() > 0 && scrollBar->value() == scrollBar->maximum()) |
| q->setOffsetToLastSection(); |
| else |
| q->setOffsetToSectionPosition(scrollBar->value()); |
| } else { |
| q->setOffset(scrollBar->value()); |
| } |
| } |
| |
| #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 << sectionsHiddenToBitVector(); |
| 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 << sectionItems; |
| out << resizeContentsPrecision; |
| out << customDefaultSectionSize; |
| out << lastSectionSize; |
| } |
| |
| bool QHeaderViewPrivate::read(QDataStream &in) |
| { |
| Q_Q(QHeaderView); |
| int orient, order, align, global; |
| int sortIndicatorSectionIn; |
| bool sortIndicatorShownIn; |
| int lengthIn; |
| QVector<int> visualIndicesIn; |
| QVector<int> logicalIndicesIn; |
| QHash<int, int> hiddenSectionSizeIn; |
| bool movableSectionsIn; |
| bool clickableSectionsIn; |
| bool highlightSelectedIn; |
| bool stretchLastSectionIn; |
| bool cascadingResizingIn; |
| int stretchSectionsIn; |
| int contentsSectionsIn; |
| int defaultSectionSizeIn; |
| int minimumSectionSizeIn; |
| QVector<SectionItem> sectionItemsIn; |
| |
| in >> orient; |
| in >> order; |
| |
| in >> sortIndicatorSectionIn; |
| in >> sortIndicatorShownIn; |
| |
| in >> visualIndicesIn; |
| in >> logicalIndicesIn; |
| |
| QBitArray sectionHidden; |
| in >> sectionHidden; |
| in >> hiddenSectionSizeIn; |
| in >> lengthIn; |
| |
| int unusedSectionCount; // For compatibility |
| in >> unusedSectionCount; |
| |
| if (in.status() != QDataStream::Ok || lengthIn < 0) |
| return false; |
| |
| in >> movableSectionsIn; |
| in >> clickableSectionsIn; |
| in >> highlightSelectedIn; |
| in >> stretchLastSectionIn; |
| in >> cascadingResizingIn; |
| in >> stretchSectionsIn; |
| in >> contentsSectionsIn; |
| in >> defaultSectionSizeIn; |
| in >> minimumSectionSizeIn; |
| |
| in >> align; |
| |
| in >> global; |
| |
| in >> sectionItemsIn; |
| // In Qt4 we had a vector of spans where one span could hold information on more sections. |
| // Now we have an itemvector where one items contains information about one section |
| // For backward compatibility with Qt4 we do the following |
| QVector<SectionItem> newSectionItems; |
| for (int u = 0; u < sectionItemsIn.count(); ++u) { |
| int count = sectionItemsIn.at(u).tmpDataStreamSectionCount; |
| if (count > 1) |
| sectionItemsIn[u].size /= count; |
| for (int n = 0; n < count; ++n) |
| newSectionItems.append(sectionItemsIn[u]); |
| } |
| |
| int sectionItemsLengthTotal = 0; |
| for (const SectionItem §ion : qAsConst(newSectionItems)) |
| sectionItemsLengthTotal += section.size; |
| if (sectionItemsLengthTotal != lengthIn) |
| return false; |
| |
| const int currentCount = (orient == Qt::Horizontal ? model->columnCount(root) : model->rowCount(root)); |
| if (newSectionItems.count() < currentCount) { |
| // we have sections not in the saved state, give them default settings |
| if (!visualIndicesIn.isEmpty() && !logicalIndicesIn.isEmpty()) { |
| for (int i = newSectionItems.count(); i < currentCount; ++i) { |
| visualIndicesIn.append(i); |
| logicalIndicesIn.append(i); |
| } |
| } |
| const int insertCount = currentCount - newSectionItems.count(); |
| const int insertLength = defaultSectionSizeIn * insertCount; |
| lengthIn += insertLength; |
| SectionItem section(defaultSectionSizeIn, globalResizeMode); |
| newSectionItems.insert(newSectionItems.count(), insertCount, section); // append |
| } |
| |
| orientation = static_cast<Qt::Orientation>(orient); |
| sortIndicatorOrder = static_cast<Qt::SortOrder>(order); |
| sortIndicatorSection = sortIndicatorSectionIn; |
| sortIndicatorShown = sortIndicatorShownIn; |
| visualIndices = visualIndicesIn; |
| logicalIndices = logicalIndicesIn; |
| hiddenSectionSize = hiddenSectionSizeIn; |
| length = lengthIn; |
| |
| movableSections = movableSectionsIn; |
| clickableSections = clickableSectionsIn; |
| highlightSelected = highlightSelectedIn; |
| stretchLastSection = stretchLastSectionIn; |
| cascadingResizing = cascadingResizingIn; |
| stretchSections = stretchSectionsIn; |
| contentsSections = contentsSectionsIn; |
| defaultSectionSize = defaultSectionSizeIn; |
| minimumSectionSize = minimumSectionSizeIn; |
| |
| defaultAlignment = Qt::Alignment(align); |
| globalResizeMode = static_cast<QHeaderView::ResizeMode>(global); |
| |
| sectionItems = newSectionItems; |
| setHiddenSectionsFromBitVector(sectionHidden); |
| recalcSectionStartPos(); |
| |
| int tmpint; |
| in >> tmpint; |
| if (in.status() == QDataStream::Ok) // we haven't read past end |
| resizeContentsPrecision = tmpint; |
| |
| bool tmpbool; |
| in >> tmpbool; |
| if (in.status() == QDataStream::Ok) { // we haven't read past end |
| customDefaultSectionSize = tmpbool; |
| if (!customDefaultSectionSize) |
| updateDefaultSectionSizeFromStyle(); |
| } |
| |
| lastSectionSize = -1; |
| int inLastSectionSize; |
| in >> inLastSectionSize; |
| if (in.status() == QDataStream::Ok) |
| lastSectionSize = inLastSectionSize; |
| |
| lastSectionLogicalIdx = -1; |
| if (stretchLastSection) { |
| lastSectionLogicalIdx = q->logicalIndex(lastVisibleVisualIndex()); |
| doDelayedResizeSections(); |
| } |
| |
| return true; |
| } |
| |
| #endif // QT_NO_DATASTREAM |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qheaderview.cpp" |