1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qheaderview.h"
43 
44 #ifndef QT_NO_ITEMVIEWS
45 #include <qbitarray.h>
46 #include <qbrush.h>
47 #include <qdebug.h>
48 #include <qevent.h>
49 #include <qpainter.h>
50 #include <qscrollbar.h>
51 #include <qtooltip.h>
52 #include <qwhatsthis.h>
53 #include <qstyle.h>
54 #include <qstyleoption.h>
55 #include <qvector.h>
56 #include <qapplication.h>
57 #include <qvarlengtharray.h>
58 #include <qabstractitemdelegate.h>
59 #include <qvariant.h>
60 #include <private/qheaderview_p.h>
61 #include <private/qabstractitemmodel_p.h>
62 
63 #ifndef QT_NO_DATASTREAM
64 #include <qdatastream.h>
65 #endif
66 
67 QT_BEGIN_NAMESPACE
68 
69 #ifndef QT_NO_DATASTREAM
operator <<(QDataStream & out,const QHeaderViewPrivate::SectionSpan & span)70 QDataStream &operator<<(QDataStream &out, const QHeaderViewPrivate::SectionSpan &span)
71 {
72     span.write(out);
73     return out;
74 }
75 
operator >>(QDataStream & in,QHeaderViewPrivate::SectionSpan & span)76 QDataStream &operator>>(QDataStream &in, QHeaderViewPrivate::SectionSpan &span)
77 {
78     span.read(in);
79     return in;
80 }
81 #endif // QT_NO_DATASTREAM
82 
83 
84 /*!
85     \class QHeaderView
86 
87     \brief The QHeaderView class provides a header row or header column for
88     item views.
89 
90     \ingroup model-view
91 
92 
93     A QHeaderView displays the headers used in item views such as the
94     QTableView and QTreeView classes. It takes the place of Qt3's \c QHeader
95     class previously used for the same purpose, but uses the Qt's model/view
96     architecture for consistency with the item view classes.
97 
98     The QHeaderView class is one of the \l{Model/View Classes} and is part of
99     Qt's \l{Model/View Programming}{model/view framework}.
100 
101     The header gets the data for each section from the model using the
102     QAbstractItemModel::headerData() function. You can set the data by using
103     QAbstractItemModel::setHeaderData().
104 
105     Each header has an orientation() and a number of sections, given by the
106     count() function. A section refers to a part of the header - either a row
107     or a column, depending on the orientation.
108 
109     Sections can be moved and resized using moveSection() and resizeSection();
110     they can also be hidden and shown with hideSection() and showSection().
111 
112     Each section of a header is described by a section ID, specified by its
113     section(), and can be located at a particular visualIndex() in the header.
114     A section can have a sort indicator set with setSortIndicator(); this
115     indicates whether the items in the associated item view will be sorted in
116     the order given by the section.
117 
118     For a horizontal header the section is equivalent to a column in the model,
119     and for a vertical header the section is equivalent to a row in the model.
120 
121     \section1 Moving Header Sections
122 
123     A header can be fixed in place, or made movable with setMovable(). It can
124     be made clickable with setClickable(), and has resizing behavior in
125     accordance with setResizeMode().
126 
127     \note Double-clicking on a header to resize a section only applies for
128     visible rows.
129 
130     A header will emit sectionMoved() if the user moves a section,
131     sectionResized() if the user resizes a section, and sectionClicked() as
132     well as sectionHandleDoubleClicked() in response to mouse clicks. A header
133     will also emit sectionCountChanged() and sectionAutoResize().
134 
135     You can identify a section using the logicalIndex() and logicalIndexAt()
136     functions, or by its index position, using the visualIndex() and
137     visualIndexAt() functions. The visual index will change if a section is
138     moved, but the logical index will not change.
139 
140     \section1 Appearance
141 
142     QTableWidget and QTableView create default headers. If you want
143     the headers to be visible, you can use \l{QFrame::}{setVisible()}.
144 
145     Not all \l{Qt::}{ItemDataRole}s will have an effect on a
146     QHeaderView. If you need to draw other roles, you can subclass
147     QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
148     QHeaderView respects the following item data roles:
149     \l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
150     \l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
151     \l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
152 
153     \note Each header renders the data for each section itself, and does not
154     rely on a delegate. As a result, calling a header's setItemDelegate()
155     function will have no effect.
156 
157     \sa {Model/View Programming}, QListView, QTableView, QTreeView
158 */
159 
160 /*!
161     \enum QHeaderView::ResizeMode
162 
163     The resize mode specifies the behavior of the header sections. It can be
164     set on the entire header view or on individual sections using
165     setResizeMode().
166 
167     \value Interactive The user can resize the section. The section can also be
168            resized programmatically using resizeSection().  The section size
169            defaults to \l defaultSectionSize. (See also
170            \l cascadingSectionResizes.)
171 
172     \value Fixed The user cannot resize the section. The section can only be
173            resized programmatically using resizeSection(). The section size
174            defaults to \l defaultSectionSize.
175 
176     \value Stretch QHeaderView will automatically resize the section to fill
177            the available space. The size cannot be changed by the user or
178            programmatically.
179 
180     \value ResizeToContents QHeaderView will automatically resize the section
181            to its optimal size based on the contents of the entire column or
182            row. The size cannot be changed by the user or programmatically.
183            (This value was introduced in 4.2)
184 
185     The following values are obsolete:
186     \value Custom Use Fixed instead.
187 
188     \sa setResizeMode() stretchLastSection minimumSectionSize
189 */
190 
191 /*!
192     \fn void QHeaderView::sectionMoved(int logicalIndex, int oldVisualIndex,
193     int newVisualIndex)
194 
195     This signal is emitted when a section is moved. The section's logical index
196     is specified by \a logicalIndex, the old index by \a oldVisualIndex, and
197     the new index position by \a newVisualIndex.
198 
199     \sa moveSection()
200 */
201 
202 /*!
203     \fn void QHeaderView::sectionResized(int logicalIndex, int oldSize,
204     int newSize)
205 
206     This signal is emitted when a section is resized. The section's logical
207     number is specified by \a logicalIndex, the old size by \a oldSize, and the
208     new size by \a newSize.
209 
210     \sa resizeSection()
211 */
212 
213 /*!
214     \fn void QHeaderView::sectionPressed(int logicalIndex)
215 
216     This signal is emitted when a section is pressed. The section's logical
217     index is specified by \a logicalIndex.
218 
219     \sa setClickable()
220 */
221 
222 /*!
223     \fn void QHeaderView::sectionClicked(int logicalIndex)
224 
225     This signal is emitted when a section is clicked. The section's logical
226     index is specified by \a logicalIndex.
227 
228     Note that the sectionPressed signal will also be emitted.
229 
230     \sa setClickable(), sectionPressed()
231 */
232 
233 /*!
234     \fn void QHeaderView::sectionEntered(int logicalIndex)
235     \since 4.3
236 
237     This signal is emitted when the cursor moves over the section and the left
238     mouse button is pressed. The section's logical index is specified by
239     \a logicalIndex.
240 
241     \sa setClickable(), sectionPressed()
242 */
243 
244 /*!
245     \fn void QHeaderView::sectionDoubleClicked(int logicalIndex)
246 
247     This signal is emitted when a section is double-clicked. The section's
248     logical index is specified by \a logicalIndex.
249 
250     \sa setClickable()
251 */
252 
253 /*!
254     \fn void QHeaderView::sectionCountChanged(int oldCount, int newCount)
255 
256     This signal is emitted when the number of sections changes, i.e., when
257     sections are added or deleted. The original count is specified by
258     \a oldCount, and the new count by \a newCount.
259 
260     \sa count(), length(), headerDataChanged()
261 */
262 
263 /*!
264     \fn void QHeaderView::sectionHandleDoubleClicked(int logicalIndex)
265 
266     This signal is emitted when a section is double-clicked. The section's
267     logical index is specified by \a logicalIndex.
268 
269     \sa setClickable()
270 */
271 
272 /*!
273     \fn void QHeaderView::sortIndicatorChanged(int logicalIndex,
274     Qt::SortOrder order)
275     \since 4.3
276 
277     This signal is emitted when the section containing the sort indicator or
278     the order indicated is changed. The section's logical index is specified
279     by \a logicalIndex and the sort order is specified by \a order.
280 
281     \sa setSortIndicator()
282 */
283 
284 /*!
285     \fn void QHeaderView::sectionAutoResize(int logicalIndex,
286     QHeaderView::ResizeMode mode)
287 
288     This signal is emitted when a section is automatically resized. The
289     section's logical index is specified by \a logicalIndex, and the resize
290     mode by \a mode.
291 
292     \sa setResizeMode(), stretchLastSection()
293 */
294 // ### Qt 5: change to sectionAutoResized()
295 
296 /*!
297     \fn void QHeaderView::geometriesChanged()
298     \since 4.2
299 
300     This signal is emitted when the header's geometries have changed.
301 */
302 
303 /*!
304     \property QHeaderView::highlightSections
305     \brief whether the sections containing selected items are highlighted
306 
307     By default, this property is false.
308 */
309 
310 /*!
311     Creates a new generic header with the given \a orientation and \a parent.
312 */
QHeaderView(Qt::Orientation orientation,QWidget * parent)313 QHeaderView::QHeaderView(Qt::Orientation orientation, QWidget *parent)
314     : QAbstractItemView(*new QHeaderViewPrivate, parent)
315 {
316     Q_D(QHeaderView);
317     d->setDefaultValues(orientation);
318     initialize();
319 }
320 
321 /*!
322   \internal
323 */
QHeaderView(QHeaderViewPrivate & dd,Qt::Orientation orientation,QWidget * parent)324 QHeaderView::QHeaderView(QHeaderViewPrivate &dd,
325                          Qt::Orientation orientation, QWidget *parent)
326     : QAbstractItemView(dd, parent)
327 {
328     Q_D(QHeaderView);
329     d->setDefaultValues(orientation);
330     initialize();
331 }
332 
333 /*!
334   Destroys the header.
335 */
336 
~QHeaderView()337 QHeaderView::~QHeaderView()
338 {
339 }
340 
341 /*!
342   \internal
343 */
initialize()344 void QHeaderView::initialize()
345 {
346     Q_D(QHeaderView);
347     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
348     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
349     setFrameStyle(NoFrame);
350     setFocusPolicy(Qt::NoFocus);
351     d->viewport->setMouseTracking(true);
352     d->viewport->setBackgroundRole(QPalette::Button);
353     d->textElideMode = Qt::ElideNone;
354     delete d->itemDelegate;
355 }
356 
357 /*!
358   \reimp
359 */
setModel(QAbstractItemModel * model)360 void QHeaderView::setModel(QAbstractItemModel *model)
361 {
362     if (model == this->model())
363         return;
364     Q_D(QHeaderView);
365     if (d->model && d->model != QAbstractItemModelPrivate::staticEmptyModel()) {
366     if (d->orientation == Qt::Horizontal) {
367         QObject::disconnect(d->model, SIGNAL(columnsInserted(QModelIndex,int,int)),
368                             this, SLOT(sectionsInserted(QModelIndex,int,int)));
369         QObject::disconnect(d->model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
370                             this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
371         QObject::disconnect(d->model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
372                             this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
373     } else {
374         QObject::disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
375                             this, SLOT(sectionsInserted(QModelIndex,int,int)));
376         QObject::disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
377                             this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
378         QObject::disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
379                             this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
380     }
381     QObject::disconnect(d->model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
382                         this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
383         QObject::disconnect(d->model, SIGNAL(layoutAboutToBeChanged()),
384                             this, SLOT(_q_layoutAboutToBeChanged()));
385     }
386 
387     if (model && model != QAbstractItemModelPrivate::staticEmptyModel()) {
388         if (d->orientation == Qt::Horizontal) {
389             QObject::connect(model, SIGNAL(columnsInserted(QModelIndex,int,int)),
390                              this, SLOT(sectionsInserted(QModelIndex,int,int)));
391             QObject::connect(model, SIGNAL(columnsAboutToBeRemoved(QModelIndex,int,int)),
392                 this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
393             QObject::connect(model, SIGNAL(columnsRemoved(QModelIndex,int,int)),
394                 this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
395         } else {
396             QObject::connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
397                              this, SLOT(sectionsInserted(QModelIndex,int,int)));
398             QObject::connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
399                              this, SLOT(sectionsAboutToBeRemoved(QModelIndex,int,int)));
400             QObject::connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
401                              this, SLOT(_q_sectionsRemoved(QModelIndex,int,int)));
402         }
403         QObject::connect(model, SIGNAL(headerDataChanged(Qt::Orientation,int,int)),
404                          this, SLOT(headerDataChanged(Qt::Orientation,int,int)));
405         QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
406                          this, SLOT(_q_layoutAboutToBeChanged()));
407     }
408 
409     d->state = QHeaderViewPrivate::NoClear;
410     QAbstractItemView::setModel(model);
411     d->state = QHeaderViewPrivate::NoState;
412 
413     // Users want to set sizes and modes before the widget is shown.
414     // Thus, we have to initialize when the model is set,
415     // and not lazily like we do in the other views.
416     initializeSections();
417 }
418 
419 /*!
420     Returns the orientation of the header.
421 
422     \sa Qt::Orientation
423 */
424 
orientation() const425 Qt::Orientation QHeaderView::orientation() const
426 {
427     Q_D(const QHeaderView);
428     return d->orientation;
429 }
430 
431 /*!
432     Returns the offset of the header: this is the header's left-most (or
433     top-most for vertical headers) visible pixel.
434 
435     \sa setOffset()
436 */
437 
offset() const438 int QHeaderView::offset() const
439 {
440     Q_D(const QHeaderView);
441     return d->offset;
442 }
443 
444 /*!
445     \fn void QHeaderView::setOffset(int offset)
446 
447     Sets the header's offset to \a offset.
448 
449     \sa offset(), length()
450 */
451 
setOffset(int newOffset)452 void QHeaderView::setOffset(int newOffset)
453 {
454     Q_D(QHeaderView);
455     if (d->offset == (int)newOffset)
456         return;
457     int ndelta = d->offset - newOffset;
458     d->offset = newOffset;
459     if (d->orientation == Qt::Horizontal)
460         d->viewport->scroll(isRightToLeft() ? -ndelta : ndelta, 0);
461     else
462         d->viewport->scroll(0, ndelta);
463     if (d->state == QHeaderViewPrivate::ResizeSection) {
464         QPoint cursorPos = QCursor::pos();
465         if (d->orientation == Qt::Horizontal)
466             QCursor::setPos(cursorPos.x() + ndelta, cursorPos.y());
467         else
468             QCursor::setPos(cursorPos.x(), cursorPos.y() + ndelta);
469         d->firstPos += ndelta;
470         d->lastPos += ndelta;
471     }
472 }
473 
474 /*!
475     \since 4.2
476     Sets the offset to the start of the section at the given \a visualIndex.
477 
478     \sa setOffset(), sectionPosition()
479 */
setOffsetToSectionPosition(int visualIndex)480 void QHeaderView::setOffsetToSectionPosition(int visualIndex)
481 {
482     Q_D(QHeaderView);
483     if (visualIndex > -1 && visualIndex < d->sectionCount) {
484         int position = d->headerSectionPosition(d->adjustedVisualIndex(visualIndex));
485         setOffset(position);
486     }
487 }
488 
489 /*!
490     \since 4.2
491     Sets the offset to make the last section visible.
492 
493     \sa setOffset(), sectionPosition(), setOffsetToSectionPosition()
494 */
setOffsetToLastSection()495 void QHeaderView::setOffsetToLastSection()
496 {
497     Q_D(const QHeaderView);
498     int size = (d->orientation == Qt::Horizontal ? viewport()->width() : viewport()->height());
499     int position = length() - size;
500     setOffset(position);
501 }
502 
503 /*!
504     Returns the length along the orientation of the header.
505 
506     \sa sizeHint(), setResizeMode(), offset()
507 */
508 
length() const509 int QHeaderView::length() const
510 {
511     Q_D(const QHeaderView);
512     d->executePostedLayout();
513     d->executePostedResize();
514     //Q_ASSERT(d->headerLength() == d->length);
515     return d->length;
516 }
517 
518 /*!
519     Returns a suitable size hint for this header.
520 
521     \sa sectionSizeHint()
522 */
523 
sizeHint() const524 QSize QHeaderView::sizeHint() const
525 {
526     Q_D(const QHeaderView);
527     if (d->cachedSizeHint.isValid())
528         return d->cachedSizeHint;
529     d->cachedSizeHint = QSize(0, 0); //reinitialize the cached size hint
530     const int sectionCount = count();
531 
532     // get size hint for the first n sections
533     int i = 0;
534     for (int checked = 0; checked < 100 && i < sectionCount; ++i) {
535         if (isSectionHidden(i))
536             continue;
537         checked++;
538         QSize hint = sectionSizeFromContents(i);
539         d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
540     }
541     // get size hint for the last n sections
542     i = qMax(i, sectionCount - 100 );
543     for (int j = sectionCount - 1, checked = 0; j >= i && checked < 100; --j) {
544         if (isSectionHidden(j))
545             continue;
546         checked++;
547         QSize hint = sectionSizeFromContents(j);
548         d->cachedSizeHint = d->cachedSizeHint.expandedTo(hint);
549     }
550     return d->cachedSizeHint;
551 }
552 
553 /*!
554     Returns a suitable size hint for the section specified by \a logicalIndex.
555 
556     \sa sizeHint(), defaultSectionSize(), minimumSectionSize(),
557     Qt::SizeHintRole
558 */
559 
sectionSizeHint(int logicalIndex) const560 int QHeaderView::sectionSizeHint(int logicalIndex) const
561 {
562     Q_D(const QHeaderView);
563     if (isSectionHidden(logicalIndex))
564         return 0;
565     if (logicalIndex < 0 || logicalIndex >= count())
566         return -1;
567     QSize size;
568     QVariant value = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
569     if (value.isValid())
570         size = qvariant_cast<QSize>(value);
571     else
572         size = sectionSizeFromContents(logicalIndex);
573     int hint = d->orientation == Qt::Horizontal ? size.width() : size.height();
574     return qMax(minimumSectionSize(), hint);
575 }
576 
577 /*!
578     Returns the visual index of the section that covers the given \a position
579     in the viewport.
580 
581     \sa logicalIndexAt()
582 */
583 
visualIndexAt(int position) const584 int QHeaderView::visualIndexAt(int position) const
585 {
586     Q_D(const QHeaderView);
587     int vposition = position;
588     d->executePostedLayout();
589     d->executePostedResize();
590     const int count = d->sectionCount;
591     if (count < 1)
592         return -1;
593 
594     if (d->reverse())
595         vposition = d->viewport->width() - vposition;
596     vposition += d->offset;
597 
598     if (vposition > d->length)
599         return -1;
600     int visual = d->headerVisualIndexAt(vposition);
601     if (visual < 0)
602         return -1;
603 
604     while (d->isVisualIndexHidden(visual)){
605         ++visual;
606         if (visual >= count)
607             return -1;
608     }
609     return visual;
610 }
611 
612 /*!
613     Returns the section that covers the given \a position in the viewport.
614 
615     \sa visualIndexAt(), isSectionHidden()
616 */
617 
logicalIndexAt(int position) const618 int QHeaderView::logicalIndexAt(int position) const
619 {
620     const int visual = visualIndexAt(position);
621     if (visual > -1)
622         return logicalIndex(visual);
623     return -1;
624 }
625 
626 /*!
627     Returns the width (or height for vertical headers) of the given
628     \a logicalIndex.
629 
630     \sa length(), setResizeMode(), defaultSectionSize()
631 */
632 
sectionSize(int logicalIndex) const633 int QHeaderView::sectionSize(int logicalIndex) const
634 {
635     Q_D(const QHeaderView);
636     if (isSectionHidden(logicalIndex))
637         return 0;
638     if (logicalIndex < 0 || logicalIndex >= count())
639         return 0;
640     int visual = visualIndex(logicalIndex);
641     if (visual == -1)
642         return 0;
643     d->executePostedResize();
644     return d->headerSectionSize(visual);
645 }
646 
647 /*!
648 
649     Returns the section position of the given \a logicalIndex, or -1
650     if the section is hidden. The position is measured in pixels from
651     the first visible item's top-left corner to the top-left corner of
652     the item with \a logicalIndex. The measurement is along the x-axis
653     for horizontal headers and along the y-axis for vertical headers.
654 
655     \sa sectionViewportPosition()
656 */
657 
sectionPosition(int logicalIndex) const658 int QHeaderView::sectionPosition(int logicalIndex) const
659 {
660     Q_D(const QHeaderView);
661     int visual = visualIndex(logicalIndex);
662     // in some cases users may change the selections
663     // before we have a chance to do the layout
664     if (visual == -1)
665         return -1;
666     d->executePostedResize();
667     return d->headerSectionPosition(visual);
668 }
669 
670 /*!
671     Returns the section viewport position of the given \a logicalIndex.
672 
673     If the section is hidden, the return value is undefined.
674 
675     \sa sectionPosition(), isSectionHidden()
676 */
677 
sectionViewportPosition(int logicalIndex) const678 int QHeaderView::sectionViewportPosition(int logicalIndex) const
679 {
680     Q_D(const QHeaderView);
681     if (logicalIndex >= count())
682         return -1;
683     int position = sectionPosition(logicalIndex);
684     if (position < 0)
685         return position; // the section was hidden
686     int offsetPosition = position - d->offset;
687     if (d->reverse())
688         return d->viewport->width() - (offsetPosition + sectionSize(logicalIndex));
689     return offsetPosition;
690 }
691 
692 /*!
693     \fn int QHeaderView::logicalIndexAt(int x, int y) const
694 
695     Returns the logical index of the section at the given coordinate. If the
696     header is horizontal \a x will be used, otherwise \a y will be used to
697     find the logical index.
698 */
699 
700 /*!
701     \fn int QHeaderView::logicalIndexAt(const QPoint &pos) const
702 
703     Returns the logical index of the section at the position given in \a pos.
704     If the header is horizontal the x-coordinate will be used, otherwise the
705     y-coordinate will be used to find the logical index.
706 
707     \sa sectionPosition()
708 */
709 
710 /*!
711     Moves the section at visual index \a from to occupy visual index \a to.
712 
713     \sa sectionsMoved()
714 */
715 
moveSection(int from,int to)716 void QHeaderView::moveSection(int from, int to)
717 {
718     Q_D(QHeaderView);
719 
720     d->executePostedLayout();
721     if (from < 0 || from >= d->sectionCount || to < 0 || to >= d->sectionCount)
722         return;
723 
724     if (from == to) {
725         int logical = logicalIndex(from);
726         Q_ASSERT(logical != -1);
727         updateSection(logical);
728         return;
729     }
730 
731     if (stretchLastSection() &&  to == d->lastVisibleVisualIndex())
732         d->lastSectionSize = sectionSize(from);
733 
734     //int oldHeaderLength = length(); // ### for debugging; remove later
735     d->initializeIndexMapping();
736 
737     QBitArray sectionHidden = d->sectionHidden;
738     int *visualIndices = d->visualIndices.data();
739     int *logicalIndices = d->logicalIndices.data();
740     int logical = logicalIndices[from];
741     int visual = from;
742 
743     int affected_count = qAbs(to - from) + 1;
744     QVarLengthArray<int> sizes(affected_count);
745     QVarLengthArray<ResizeMode> modes(affected_count);
746 
747     // move sections and indices
748     if (to > from) {
749         sizes[to - from] = d->headerSectionSize(from);
750         modes[to - from] = d->headerSectionResizeMode(from);
751         while (visual < to) {
752             sizes[visual - from] = d->headerSectionSize(visual + 1);
753             modes[visual - from] = d->headerSectionResizeMode(visual + 1);
754             if (!sectionHidden.isEmpty())
755                 sectionHidden.setBit(visual, sectionHidden.testBit(visual + 1));
756             visualIndices[logicalIndices[visual + 1]] = visual;
757             logicalIndices[visual] = logicalIndices[visual + 1];
758             ++visual;
759         }
760     } else {
761         sizes[0] = d->headerSectionSize(from);
762         modes[0] = d->headerSectionResizeMode(from);
763         while (visual > to) {
764             sizes[visual - to] = d->headerSectionSize(visual - 1);
765             modes[visual - to] = d->headerSectionResizeMode(visual - 1);
766             if (!sectionHidden.isEmpty())
767                 sectionHidden.setBit(visual, sectionHidden.testBit(visual - 1));
768             visualIndices[logicalIndices[visual - 1]] = visual;
769             logicalIndices[visual] = logicalIndices[visual - 1];
770             --visual;
771         }
772     }
773     if (!sectionHidden.isEmpty()) {
774         sectionHidden.setBit(to, d->sectionHidden.testBit(from));
775         d->sectionHidden = sectionHidden;
776     }
777     visualIndices[logical] = to;
778     logicalIndices[to] = logical;
779 
780     //Q_ASSERT(oldHeaderLength == length());
781     // move sizes
782     // ### check for spans of section sizes here
783     if (to > from) {
784         for (visual = from; visual <= to; ++visual) {
785             int size = sizes[visual - from];
786             ResizeMode mode = modes[visual - from];
787             d->createSectionSpan(visual, visual, size, mode);
788         }
789     } else {
790         for (visual = to; visual <= from; ++visual) {
791             int size = sizes[visual - to];
792             ResizeMode mode = modes[visual - to];
793             d->createSectionSpan(visual, visual, size, mode);
794         }
795     }
796     //Q_ASSERT(d->headerLength() == length());
797     //Q_ASSERT(oldHeaderLength == length());
798     //Q_ASSERT(d->logicalIndices.count() == d->sectionCount);
799 
800     if (d->hasAutoResizeSections())
801         d->doDelayedResizeSections();
802     d->viewport->update();
803 
804     emit sectionMoved(logical, from, to);
805 }
806 
807 /*!
808     \since 4.2
809     Swaps the section at visual index \a first with the section at visual
810     index \a second.
811 
812     \sa moveSection()
813 */
swapSections(int first,int second)814 void QHeaderView::swapSections(int first, int second)
815 {
816     Q_D(QHeaderView);
817 
818     if (first == second)
819         return;
820     d->executePostedLayout();
821     if (first < 0 || first >= d->sectionCount || second < 0 || second >= d->sectionCount)
822         return;
823 
824     int firstSize = d->headerSectionSize(first);
825     ResizeMode firstMode = d->headerSectionResizeMode(first);
826     int firstLogical = d->logicalIndex(first);
827 
828     int secondSize = d->headerSectionSize(second);
829     ResizeMode secondMode = d->headerSectionResizeMode(second);
830     int secondLogical = d->logicalIndex(second);
831 
832     d->createSectionSpan(second, second, firstSize, firstMode);
833     d->createSectionSpan(first, first, secondSize, secondMode);
834 
835     d->initializeIndexMapping();
836 
837     d->visualIndices[firstLogical] = second;
838     d->logicalIndices[second] = firstLogical;
839 
840     d->visualIndices[secondLogical] = first;
841     d->logicalIndices[first] = secondLogical;
842 
843     if (!d->sectionHidden.isEmpty()) {
844         bool firstHidden = d->sectionHidden.testBit(first);
845         bool secondHidden = d->sectionHidden.testBit(second);
846         d->sectionHidden.setBit(first, secondHidden);
847         d->sectionHidden.setBit(second, firstHidden);
848     }
849 
850     d->viewport->update();
851     emit sectionMoved(firstLogical, first, second);
852     emit sectionMoved(secondLogical, second, first);
853 }
854 
855 /*!
856     \fn void QHeaderView::resizeSection(int logicalIndex, int size)
857 
858     Resizes the section specified by \a logicalIndex to \a size measured in
859     pixels.
860 
861     \sa sectionResized(), resizeMode(), sectionSize()
862 */
863 
resizeSection(int logical,int size)864 void QHeaderView::resizeSection(int logical, int size)
865 {
866     Q_D(QHeaderView);
867     if (logical < 0 || logical >= count())
868         return;
869 
870     if (isSectionHidden(logical)) {
871         d->hiddenSectionSize.insert(logical, size);
872         return;
873     }
874 
875     int visual = visualIndex(logical);
876     if (visual == -1)
877         return;
878 
879     int oldSize = d->headerSectionSize(visual);
880     if (oldSize == size)
881         return;
882 
883     d->executePostedLayout();
884     d->invalidateCachedSizeHint();
885 
886     if (stretchLastSection() && visual == d->lastVisibleVisualIndex())
887         d->lastSectionSize = size;
888 
889     if (size != oldSize)
890         d->createSectionSpan(visual, visual, size, d->headerSectionResizeMode(visual));
891 
892     if (!updatesEnabled()) {
893         if (d->hasAutoResizeSections())
894             d->doDelayedResizeSections();
895         emit sectionResized(logical, oldSize, size);
896         return;
897     }
898 
899     int w = d->viewport->width();
900     int h = d->viewport->height();
901     int pos = sectionViewportPosition(logical);
902     QRect r;
903     if (d->orientation == Qt::Horizontal)
904         if (isRightToLeft())
905             r.setRect(0, 0, pos + size, h);
906         else
907             r.setRect(pos, 0, w - pos, h);
908     else
909         r.setRect(0, pos, w, h - pos);
910 
911     if (d->hasAutoResizeSections()) {
912         d->doDelayedResizeSections();
913         r = d->viewport->rect();
914     }
915     d->viewport->update(r.normalized());
916     emit sectionResized(logical, oldSize, size);
917 }
918 
919 /*!
920     Resizes the sections according to the given \a mode, ignoring the current
921     resize mode.
922 
923     \sa resizeMode(), sectionResized()
924 */
925 
resizeSections(QHeaderView::ResizeMode mode)926 void QHeaderView::resizeSections(QHeaderView::ResizeMode mode)
927 {
928     Q_D(QHeaderView);
929     d->resizeSections(mode, true);
930 }
931 
932 /*!
933     \fn void QHeaderView::hideSection(int logicalIndex)
934     Hides the section specified by \a logicalIndex.
935 
936     \sa showSection(), isSectionHidden(), hiddenSectionCount(),
937     setSectionHidden()
938 */
939 
940 /*!
941     \fn void QHeaderView::showSection(int logicalIndex)
942     Shows the section specified by \a logicalIndex.
943 
944     \sa hideSection(), isSectionHidden(), hiddenSectionCount(),
945     setSectionHidden()
946 */
947 
948 /*!
949     Returns true if the section specified by \a logicalIndex is explicitly
950     hidden from the user; otherwise returns false.
951 
952     \sa hideSection(), showSection(), setSectionHidden(), hiddenSectionCount()
953 */
954 
isSectionHidden(int logicalIndex) const955 bool QHeaderView::isSectionHidden(int logicalIndex) const
956 {
957     Q_D(const QHeaderView);
958     d->executePostedLayout();
959     if (logicalIndex >= d->sectionHidden.count() || logicalIndex < 0 || logicalIndex >= d->sectionCount)
960         return false;
961     int visual = visualIndex(logicalIndex);
962     Q_ASSERT(visual != -1);
963     return d->sectionHidden.testBit(visual);
964 }
965 
966 /*!
967     \since 4.1
968 
969     Returns the number of sections in the header that has been hidden.
970 
971     \sa setSectionHidden(), isSectionHidden()
972 */
hiddenSectionCount() const973 int QHeaderView::hiddenSectionCount() const
974 {
975     Q_D(const QHeaderView);
976     return d->hiddenSectionSize.count();
977 }
978 
979 /*!
980   If \a hide is true the section specified by \a logicalIndex is hidden;
981   otherwise the section is shown.
982 
983   \sa isSectionHidden(), hiddenSectionCount()
984 */
985 
setSectionHidden(int logicalIndex,bool hide)986 void QHeaderView::setSectionHidden(int logicalIndex, bool hide)
987 {
988     Q_D(QHeaderView);
989     if (logicalIndex < 0 || logicalIndex >= count())
990         return;
991 
992     d->executePostedLayout();
993     int visual = visualIndex(logicalIndex);
994     Q_ASSERT(visual != -1);
995     if (hide == d->isVisualIndexHidden(visual))
996         return;
997     if (hide) {
998         int size = d->headerSectionSize(visual);
999         if (!d->hasAutoResizeSections())
1000             resizeSection(logicalIndex, 0);
1001         d->hiddenSectionSize.insert(logicalIndex, size);
1002         if (d->sectionHidden.count() < count())
1003             d->sectionHidden.resize(count());
1004         d->sectionHidden.setBit(visual, true);
1005         if (d->hasAutoResizeSections())
1006             d->doDelayedResizeSections();
1007     } else {
1008         int size = d->hiddenSectionSize.value(logicalIndex, d->defaultSectionSize);
1009         d->hiddenSectionSize.remove(logicalIndex);
1010         if (d->hiddenSectionSize.isEmpty()) {
1011             d->sectionHidden.clear();
1012         } else {
1013             Q_ASSERT(visual <= d->sectionHidden.count());
1014             d->sectionHidden.setBit(visual, false);
1015         }
1016         resizeSection(logicalIndex, size);
1017     }
1018 }
1019 
1020 /*!
1021     Returns the number of sections in the header.
1022 
1023     \sa  sectionCountChanged(), length()
1024 */
1025 
count() const1026 int QHeaderView::count() const
1027 {
1028     Q_D(const QHeaderView);
1029     //Q_ASSERT(d->sectionCount == d->headerSectionCount());
1030     // ### this may affect the lazy layout
1031     d->executePostedLayout();
1032     return d->sectionCount;
1033 }
1034 
1035 /*!
1036     Returns the visual index position of the section specified by the given
1037     \a logicalIndex, or -1 otherwise.
1038 
1039     Hidden sections still have valid visual indexes.
1040 
1041     \sa logicalIndex()
1042 */
1043 
visualIndex(int logicalIndex) const1044 int QHeaderView::visualIndex(int logicalIndex) const
1045 {
1046     Q_D(const QHeaderView);
1047     if (logicalIndex < 0)
1048         return -1;
1049     d->executePostedLayout();
1050     if (d->visualIndices.isEmpty()) { // nothing has been moved, so we have no mapping
1051         if (logicalIndex < d->sectionCount)
1052             return logicalIndex;
1053     } else if (logicalIndex < d->visualIndices.count()) {
1054         int visual = d->visualIndices.at(logicalIndex);
1055         Q_ASSERT(visual < d->sectionCount);
1056         return visual;
1057     }
1058     return -1;
1059 }
1060 
1061 /*!
1062     Returns the logicalIndex for the section at the given \a visualIndex
1063     position, or -1 if visualIndex < 0 or visualIndex >= QHeaderView::count().
1064 
1065     Note that the visualIndex is not affected by hidden sections.
1066 
1067     \sa visualIndex(), sectionPosition()
1068 */
1069 
logicalIndex(int visualIndex) const1070 int QHeaderView::logicalIndex(int visualIndex) const
1071 {
1072     Q_D(const QHeaderView);
1073     if (visualIndex < 0 || visualIndex >= d->sectionCount)
1074         return -1;
1075     return d->logicalIndex(visualIndex);
1076 }
1077 
1078 /*!
1079     If \a movable is true, the header may be moved by the user; otherwise it
1080     is fixed in place.
1081 
1082     \sa isMovable(), sectionMoved()
1083 */
1084 
1085 // ### Qt 5: change to setSectionsMovable()
setMovable(bool movable)1086 void QHeaderView::setMovable(bool movable)
1087 {
1088     Q_D(QHeaderView);
1089     d->movableSections = movable;
1090 }
1091 
1092 /*!
1093     Returns true if the header can be moved by the user; otherwise returns
1094     false.
1095 
1096     \sa setMovable()
1097 */
1098 
1099 // ### Qt 5: change to sectionsMovable()
isMovable() const1100 bool QHeaderView::isMovable() const
1101 {
1102     Q_D(const QHeaderView);
1103     return d->movableSections;
1104 }
1105 
1106 /*!
1107     If \a clickable is true, the header will respond to single clicks.
1108 
1109     \sa isClickable(), sectionClicked(), sectionPressed(),
1110     setSortIndicatorShown()
1111 */
1112 
1113 // ### Qt 5: change to setSectionsClickable()
setClickable(bool clickable)1114 void QHeaderView::setClickable(bool clickable)
1115 {
1116     Q_D(QHeaderView);
1117     d->clickableSections = clickable;
1118 }
1119 
1120 /*!
1121     Returns true if the header is clickable; otherwise returns false. A
1122     clickable header could be set up to allow the user to change the
1123     representation of the data in the view related to the header.
1124 
1125     \sa setClickable()
1126 */
1127 
1128 // ### Qt 5: change to sectionsClickable()
isClickable() const1129 bool QHeaderView::isClickable() const
1130 {
1131     Q_D(const QHeaderView);
1132     return d->clickableSections;
1133 }
1134 
setHighlightSections(bool highlight)1135 void QHeaderView::setHighlightSections(bool highlight)
1136 {
1137     Q_D(QHeaderView);
1138     d->highlightSelected = highlight;
1139 }
1140 
highlightSections() const1141 bool QHeaderView::highlightSections() const
1142 {
1143     Q_D(const QHeaderView);
1144     return d->highlightSelected;
1145 }
1146 
1147 /*!
1148     Sets the constraints on how the header can be resized to those described
1149     by the given \a mode.
1150 
1151     \sa resizeMode(), length(), sectionResized(), sectionAutoResize()
1152 */
1153 
setResizeMode(ResizeMode mode)1154 void QHeaderView::setResizeMode(ResizeMode mode)
1155 {
1156     Q_D(QHeaderView);
1157     initializeSections();
1158     d->stretchSections = (mode == Stretch ? count() : 0);
1159     d->contentsSections =  (mode == ResizeToContents ? count() : 0);
1160     d->setGlobalHeaderResizeMode(mode);
1161     if (d->hasAutoResizeSections())
1162         d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1163 }
1164 
1165 /*!
1166     \overload
1167 
1168     Sets the constraints on how the section specified by \a logicalIndex in
1169     the header can be resized to those described by the given \a mode. The logical
1170     index should exist at the time this function is called.
1171 
1172     \note This setting will be ignored for the last section if the stretchLastSection
1173     property is set to true. This is the default for the horizontal headers provided
1174     by QTreeView.
1175 
1176     \sa setStretchLastSection()
1177 */
1178 
1179 // ### Qt 5: change to setSectionResizeMode()
setResizeMode(int logicalIndex,ResizeMode mode)1180 void QHeaderView::setResizeMode(int logicalIndex, ResizeMode mode)
1181 {
1182     Q_D(QHeaderView);
1183     int visual = visualIndex(logicalIndex);
1184     Q_ASSERT(visual != -1);
1185 
1186     ResizeMode old = d->headerSectionResizeMode(visual);
1187     d->setHeaderSectionResizeMode(visual, mode);
1188 
1189     if (mode == Stretch && old != Stretch)
1190         ++d->stretchSections;
1191     else if (mode == ResizeToContents && old != ResizeToContents)
1192         ++d->contentsSections;
1193     else if (mode != Stretch && old == Stretch)
1194         --d->stretchSections;
1195     else if (mode != ResizeToContents && old == ResizeToContents)
1196         --d->contentsSections;
1197 
1198     if (d->hasAutoResizeSections() && d->state == QHeaderViewPrivate::NoState)
1199         d->doDelayedResizeSections(); // section sizes may change as a result of the new mode
1200 }
1201 
1202 /*!
1203     Returns the resize mode that applies to the section specified by the given
1204     \a logicalIndex.
1205 
1206     \sa setResizeMode()
1207 */
1208 
resizeMode(int logicalIndex) const1209 QHeaderView::ResizeMode QHeaderView::resizeMode(int logicalIndex) const
1210 {
1211     Q_D(const QHeaderView);
1212     int visual = visualIndex(logicalIndex);
1213     if (visual == -1)
1214         return Fixed; //the default value
1215     return d->headerSectionResizeMode(visual);
1216 }
1217 
1218 /*!
1219     \since 4.1
1220 
1221     Returns the number of sections that are set to resize mode stretch. In
1222     views, this can be used to see if the headerview needs to resize the
1223     sections when the view's geometry changes.
1224 
1225     \sa stretchLastSection, resizeMode()
1226 */
1227 
stretchSectionCount() const1228 int QHeaderView::stretchSectionCount() const
1229 {
1230     Q_D(const QHeaderView);
1231     return d->stretchSections;
1232 }
1233 
1234 /*!
1235   \property QHeaderView::showSortIndicator
1236   \brief whether the sort indicator is shown
1237 
1238   By default, this property is false.
1239 
1240   \sa setClickable()
1241 */
1242 
setSortIndicatorShown(bool show)1243 void QHeaderView::setSortIndicatorShown(bool show)
1244 {
1245     Q_D(QHeaderView);
1246     if (d->sortIndicatorShown == show)
1247         return;
1248 
1249     d->sortIndicatorShown = show;
1250 
1251     if (sortIndicatorSection() < 0 || sortIndicatorSection() > count())
1252         return;
1253 
1254     if (d->headerSectionResizeMode(sortIndicatorSection()) == ResizeToContents)
1255         resizeSections();
1256 
1257     d->viewport->update();
1258 }
1259 
isSortIndicatorShown() const1260 bool QHeaderView::isSortIndicatorShown() const
1261 {
1262     Q_D(const QHeaderView);
1263     return d->sortIndicatorShown;
1264 }
1265 
1266 /*!
1267     Sets the sort indicator for the section specified by the given
1268     \a logicalIndex in the direction specified by \a order, and removes the
1269     sort indicator from any other section that was showing it.
1270 
1271     \a logicalIndex may be -1, in which case no sort indicator will be shown
1272     and the model will return to its natural, unsorted order. Note that not
1273     all models support this and may even crash in this case.
1274 
1275     \sa sortIndicatorSection() sortIndicatorOrder()
1276 */
1277 
setSortIndicator(int logicalIndex,Qt::SortOrder order)1278 void QHeaderView::setSortIndicator(int logicalIndex, Qt::SortOrder order)
1279 {
1280     Q_D(QHeaderView);
1281 
1282     // This is so that people can set the position of the sort indicator before the fill the model
1283     int old = d->sortIndicatorSection;
1284     d->sortIndicatorSection = logicalIndex;
1285     d->sortIndicatorOrder = order;
1286 
1287     if (logicalIndex >= d->sectionCount) {
1288         emit sortIndicatorChanged(logicalIndex, order);
1289         return; // nothing to do
1290     }
1291 
1292     if (old != logicalIndex
1293         && ((logicalIndex >= 0 && resizeMode(logicalIndex) == ResizeToContents)
1294             || old >= d->sectionCount || (old >= 0 && resizeMode(old) == ResizeToContents))) {
1295         resizeSections();
1296         d->viewport->update();
1297     } else {
1298         if (old >= 0 && old != logicalIndex)
1299             updateSection(old);
1300         if (logicalIndex >= 0)
1301             updateSection(logicalIndex);
1302     }
1303 
1304     emit sortIndicatorChanged(logicalIndex, order);
1305 }
1306 
1307 /*!
1308     Returns the logical index of the section that has a sort indicator.
1309     By default this is section 0.
1310 
1311     \sa setSortIndicator() sortIndicatorOrder() setSortIndicatorShown()
1312 */
1313 
sortIndicatorSection() const1314 int QHeaderView::sortIndicatorSection() const
1315 {
1316     Q_D(const QHeaderView);
1317     return d->sortIndicatorSection;
1318 }
1319 
1320 /*!
1321     Returns the order for the sort indicator. If no section has a sort
1322     indicator the return value of this function is undefined.
1323 
1324     \sa setSortIndicator() sortIndicatorSection()
1325 */
1326 
sortIndicatorOrder() const1327 Qt::SortOrder QHeaderView::sortIndicatorOrder() const
1328 {
1329     Q_D(const QHeaderView);
1330     return d->sortIndicatorOrder;
1331 }
1332 
1333 /*!
1334     \property QHeaderView::stretchLastSection
1335     \brief whether the last visible section in the header takes up all the
1336     available space
1337 
1338     The default value is false.
1339 
1340     \note The horizontal headers provided by QTreeView are configured with this
1341     property set to true, ensuring that the view does not waste any of the
1342     space assigned to it for its header. If this value is set to true, this
1343     property will override the resize mode set on the last section in the
1344     header.
1345 
1346     \sa setResizeMode()
1347 */
stretchLastSection() const1348 bool QHeaderView::stretchLastSection() const
1349 {
1350     Q_D(const QHeaderView);
1351     return d->stretchLastSection;
1352 }
1353 
setStretchLastSection(bool stretch)1354 void QHeaderView::setStretchLastSection(bool stretch)
1355 {
1356     Q_D(QHeaderView);
1357     d->stretchLastSection = stretch;
1358     if (d->state != QHeaderViewPrivate::NoState)
1359         return;
1360     if (stretch)
1361         resizeSections();
1362     else if (count())
1363         resizeSection(count() - 1, d->defaultSectionSize);
1364 }
1365 
1366 /*!
1367     \since 4.2
1368     \property QHeaderView::cascadingSectionResizes
1369     \brief whether interactive resizing will be cascaded to the following
1370     sections once the section being resized by the user has reached its
1371     minimum size
1372 
1373     This property only affects sections that have \l Interactive as their
1374     resize mode.
1375 
1376     The default value is false.
1377 
1378     \sa setResizeMode()
1379 */
cascadingSectionResizes() const1380 bool QHeaderView::cascadingSectionResizes() const
1381 {
1382     Q_D(const QHeaderView);
1383     return d->cascadingResizing;
1384 }
1385 
setCascadingSectionResizes(bool enable)1386 void QHeaderView::setCascadingSectionResizes(bool enable)
1387 {
1388     Q_D(QHeaderView);
1389     d->cascadingResizing = enable;
1390 }
1391 
1392 /*!
1393     \property QHeaderView::defaultSectionSize
1394     \brief the default size of the header sections before resizing.
1395 
1396     This property only affects sections that have \l Interactive or \l Fixed
1397     as their resize mode.
1398 
1399     \sa setResizeMode() minimumSectionSize
1400 */
defaultSectionSize() const1401 int QHeaderView::defaultSectionSize() const
1402 {
1403     Q_D(const QHeaderView);
1404     return d->defaultSectionSize;
1405 }
1406 
setDefaultSectionSize(int size)1407 void QHeaderView::setDefaultSectionSize(int size)
1408 {
1409     Q_D(QHeaderView);
1410     d->setDefaultSectionSize(size);
1411 }
1412 
1413 /*!
1414     \since 4.2
1415     \property QHeaderView::minimumSectionSize
1416     \brief the minimum size of the header sections.
1417 
1418     The minimum section size is the smallest section size allowed. If the
1419     minimum section size is set to -1, QHeaderView will use the maximum of
1420     the \l{QApplication::globalStrut()}{global strut} or the
1421     \l{fontMetrics()}{font metrics} size.
1422 
1423     This property is honored by all \l{ResizeMode}{resize modes}.
1424 
1425     \sa setResizeMode() defaultSectionSize
1426 */
minimumSectionSize() const1427 int QHeaderView::minimumSectionSize() const
1428 {
1429     Q_D(const QHeaderView);
1430     if (d->minimumSectionSize == -1) {
1431         QSize strut = QApplication::globalStrut();
1432         int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, 0, this);
1433         if (d->orientation == Qt::Horizontal)
1434             return qMax(strut.width(), (fontMetrics().maxWidth() + margin));
1435         return qMax(strut.height(), (fontMetrics().height() + margin));
1436     }
1437     return d->minimumSectionSize;
1438 }
1439 
setMinimumSectionSize(int size)1440 void QHeaderView::setMinimumSectionSize(int size)
1441 {
1442     Q_D(QHeaderView);
1443     d->minimumSectionSize = size;
1444 }
1445 
1446 /*!
1447     \since 4.1
1448     \property QHeaderView::defaultAlignment
1449     \brief the default alignment of the text in each header section
1450 */
1451 
defaultAlignment() const1452 Qt::Alignment QHeaderView::defaultAlignment() const
1453 {
1454     Q_D(const QHeaderView);
1455     return d->defaultAlignment;
1456 }
1457 
setDefaultAlignment(Qt::Alignment alignment)1458 void QHeaderView::setDefaultAlignment(Qt::Alignment alignment)
1459 {
1460     Q_D(QHeaderView);
1461     if (d->defaultAlignment == alignment)
1462         return;
1463 
1464     d->defaultAlignment = alignment;
1465     d->viewport->update();
1466 }
1467 
1468 /*!
1469     \internal
1470 */
doItemsLayout()1471 void QHeaderView::doItemsLayout()
1472 {
1473     initializeSections();
1474     QAbstractItemView::doItemsLayout();
1475 }
1476 
1477 /*!
1478     Returns true if sections in the header has been moved; otherwise returns
1479     false;
1480 
1481     \sa moveSection()
1482 */
sectionsMoved() const1483 bool QHeaderView::sectionsMoved() const
1484 {
1485     Q_D(const QHeaderView);
1486     return !d->visualIndices.isEmpty();
1487 }
1488 
1489 /*!
1490     \since 4.1
1491 
1492     Returns true if sections in the header has been hidden; otherwise returns
1493     false;
1494 
1495     \sa setSectionHidden()
1496 */
sectionsHidden() const1497 bool QHeaderView::sectionsHidden() const
1498 {
1499     Q_D(const QHeaderView);
1500     return !d->hiddenSectionSize.isEmpty();
1501 }
1502 
1503 #ifndef QT_NO_DATASTREAM
1504 /*!
1505     \since 4.3
1506 
1507     Saves the current state of this header view.
1508 
1509     To restore the saved state, pass the return value to restoreState().
1510 
1511     \sa restoreState()
1512 */
saveState() const1513 QByteArray QHeaderView::saveState() const
1514 {
1515     Q_D(const QHeaderView);
1516     QByteArray data;
1517     QDataStream stream(&data, QIODevice::WriteOnly);
1518     stream << QHeaderViewPrivate::VersionMarker;
1519     stream << 0; // current version is 0
1520     d->write(stream);
1521     return data;
1522 }
1523 
1524 /*!
1525     \since 4.3
1526     Restores the \a state of this header view.
1527     This function returns \c true if the state was restored; otherwise returns
1528     false.
1529 
1530     \sa saveState()
1531 */
restoreState(const QByteArray & state)1532 bool QHeaderView::restoreState(const QByteArray &state)
1533 {
1534     Q_D(QHeaderView);
1535     if (state.isEmpty())
1536         return false;
1537     QByteArray data = state;
1538     QDataStream stream(&data, QIODevice::ReadOnly);
1539     int marker;
1540     int ver;
1541     stream >> marker;
1542     stream >> ver;
1543     if (stream.status() != QDataStream::Ok
1544         || marker != QHeaderViewPrivate::VersionMarker
1545         || ver != 0) // current version is 0
1546         return false;
1547 
1548     if (d->read(stream)) {
1549         emit sortIndicatorChanged(d->sortIndicatorSection, d->sortIndicatorOrder );
1550         d->viewport->update();
1551         return true;
1552     }
1553     return false;
1554 }
1555 #endif // QT_NO_DATASTREAM
1556 
1557 /*!
1558   \reimp
1559 */
reset()1560 void QHeaderView::reset()
1561 {
1562     QAbstractItemView::reset();
1563     // it would be correct to call clear, but some apps rely
1564     // on the header keeping the sections, even after calling reset
1565     //d->clear();
1566     initializeSections();
1567 }
1568 
1569 /*!
1570     Updates the changed header sections with the given \a orientation, from
1571     \a logicalFirst to \a logicalLast inclusive.
1572 */
headerDataChanged(Qt::Orientation orientation,int logicalFirst,int logicalLast)1573 void QHeaderView::headerDataChanged(Qt::Orientation orientation, int logicalFirst, int logicalLast)
1574 {
1575     Q_D(QHeaderView);
1576     if (d->orientation != orientation)
1577         return;
1578 
1579     if (logicalFirst < 0 || logicalLast < 0 || logicalFirst >= count() || logicalLast >= count())
1580         return;
1581 
1582     d->invalidateCachedSizeHint();
1583 
1584     int firstVisualIndex = INT_MAX, lastVisualIndex = -1;
1585 
1586     for (int section = logicalFirst; section <= logicalLast; ++section) {
1587         const int visual = visualIndex(section);
1588         firstVisualIndex = qMin(firstVisualIndex, visual);
1589         lastVisualIndex =  qMax(lastVisualIndex,  visual);
1590     }
1591 
1592     d->executePostedResize();
1593     const int first = d->headerSectionPosition(firstVisualIndex),
1594               last = d->headerSectionPosition(lastVisualIndex)
1595                         + d->headerSectionSize(lastVisualIndex);
1596 
1597     if (orientation == Qt::Horizontal) {
1598         d->viewport->update(first, 0, last - first, d->viewport->height());
1599     } else {
1600         d->viewport->update(0, first, d->viewport->width(), last - first);
1601     }
1602 }
1603 
1604 /*!
1605     \internal
1606     \since 4.2
1607 
1608     Updates the section specified by the given \a logicalIndex.
1609 */
1610 
updateSection(int logicalIndex)1611 void QHeaderView::updateSection(int logicalIndex)
1612 {
1613     Q_D(QHeaderView);
1614     if (d->orientation == Qt::Horizontal)
1615         d->viewport->update(QRect(sectionViewportPosition(logicalIndex),
1616                                   0, sectionSize(logicalIndex), d->viewport->height()));
1617     else
1618         d->viewport->update(QRect(0, sectionViewportPosition(logicalIndex),
1619                                   d->viewport->width(), sectionSize(logicalIndex)));
1620 }
1621 
1622 /*!
1623     Resizes the sections according to their size hints. Normally, you do not
1624     have to call this function.
1625 */
1626 
resizeSections()1627 void QHeaderView::resizeSections()
1628 {
1629     Q_D(QHeaderView);
1630     if (d->hasAutoResizeSections())
1631         d->resizeSections(Interactive, false); // no global resize mode
1632 }
1633 
1634 /*!
1635     This slot is called when sections are inserted into the \a parent.
1636     \a logicalFirst and \a logicalLast indices signify where the new sections
1637     were inserted.
1638 
1639     If only one section is inserted, \a logicalFirst and \a logicalLast will
1640     be the same.
1641 */
1642 
sectionsInserted(const QModelIndex & parent,int logicalFirst,int logicalLast)1643 void QHeaderView::sectionsInserted(const QModelIndex &parent,
1644                                    int logicalFirst, int logicalLast)
1645 {
1646     Q_D(QHeaderView);
1647     if (parent != d->root)
1648         return; // we only handle changes in the top level
1649     int oldCount = d->sectionCount;
1650 
1651     d->invalidateCachedSizeHint();
1652 
1653     // add the new sections
1654     int insertAt = 0;
1655     for (int spanStart = 0; insertAt < d->sectionSpans.count() && spanStart < logicalFirst; ++insertAt)
1656         spanStart += d->sectionSpans.at(insertAt).count;
1657 
1658     int insertCount = logicalLast - logicalFirst + 1;
1659     d->sectionCount += insertCount;
1660 
1661     if (d->sectionSpans.isEmpty() || insertAt >= d->sectionSpans.count()) {
1662         int insertLength = d->defaultSectionSize * insertCount;
1663         d->length += insertLength;
1664         QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
1665         d->sectionSpans.append(span);
1666     } else if ((d->sectionSpans.at(insertAt).sectionSize() == d->defaultSectionSize)
1667                && d->sectionSpans.at(insertAt).resizeMode == d->globalResizeMode) {
1668         // add the new sections to an existing span
1669         int insertLength = d->sectionSpans.at(insertAt).sectionSize() * insertCount;
1670         d->length += insertLength;
1671         d->sectionSpans[insertAt].size += insertLength;
1672         d->sectionSpans[insertAt].count += insertCount;
1673     } else {
1674         // separate them out into their own span
1675         int insertLength = d->defaultSectionSize * insertCount;
1676         d->length += insertLength;
1677         QHeaderViewPrivate::SectionSpan span(insertLength, insertCount, d->globalResizeMode);
1678         d->sectionSpans.insert(insertAt, span);
1679     }
1680 
1681     // update sorting column
1682     if (d->sortIndicatorSection >= logicalFirst)
1683         d->sortIndicatorSection += insertCount;
1684 
1685     // update resize mode section counts
1686     if (d->globalResizeMode == Stretch)
1687         d->stretchSections = d->sectionCount;
1688     else if (d->globalResizeMode == ResizeToContents)
1689         d->contentsSections = d->sectionCount;
1690 
1691     // clear selection cache
1692     d->sectionSelected.clear();
1693 
1694     // update mapping
1695     if (!d->visualIndices.isEmpty() && !d->logicalIndices.isEmpty()) {
1696         Q_ASSERT(d->visualIndices.count() == d->logicalIndices.count());
1697         int mappingCount = d->visualIndices.count();
1698         for (int i = 0; i < mappingCount; ++i) {
1699             if (d->visualIndices.at(i) >= logicalFirst)
1700                d->visualIndices[i] += insertCount;
1701             if (d->logicalIndices.at(i) >= logicalFirst)
1702                 d->logicalIndices[i] += insertCount;
1703         }
1704         for (int j = logicalFirst; j <= logicalLast; ++j) {
1705             d->visualIndices.insert(j, j);
1706             d->logicalIndices.insert(j, j);
1707         }
1708     }
1709 
1710     // insert sections into sectionsHidden
1711     if (!d->sectionHidden.isEmpty()) {
1712         QBitArray sectionHidden(d->sectionHidden);
1713         sectionHidden.resize(sectionHidden.count() + insertCount);
1714         sectionHidden.fill(false, logicalFirst, logicalLast + 1);
1715         for (int j = logicalLast + 1; j < sectionHidden.count(); ++j)
1716             //here we simply copy the old sectionHidden
1717             sectionHidden.setBit(j, d->sectionHidden.testBit(j - insertCount));
1718         d->sectionHidden = sectionHidden;
1719     }
1720 
1721     // insert sections into hiddenSectionSize
1722     QHash<int, int> newHiddenSectionSize; // from logical index to section size
1723     for (int i = 0; i < logicalFirst; ++i)
1724         if (isSectionHidden(i))
1725             newHiddenSectionSize[i] = d->hiddenSectionSize[i];
1726     for (int j = logicalLast + 1; j < d->sectionCount; ++j)
1727         if (isSectionHidden(j))
1728             newHiddenSectionSize[j] = d->hiddenSectionSize[j - insertCount];
1729     d->hiddenSectionSize = newHiddenSectionSize;
1730 
1731     d->doDelayedResizeSections();
1732     emit sectionCountChanged(oldCount, count());
1733 
1734     // if the new sections were not updated by resizing, we need to update now
1735     if (!d->hasAutoResizeSections())
1736         d->viewport->update();
1737 }
1738 
1739 /*!
1740     This slot is called when sections are removed from the \a parent.
1741     \a logicalFirst and \a logicalLast signify where the sections were removed.
1742 
1743     If only one section is removed, \a logicalFirst and \a logicalLast will
1744     be the same.
1745 */
1746 
sectionsAboutToBeRemoved(const QModelIndex & parent,int logicalFirst,int logicalLast)1747 void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
1748                                            int logicalFirst, int logicalLast)
1749 {
1750     Q_UNUSED(parent);
1751     Q_UNUSED(logicalFirst);
1752     Q_UNUSED(logicalLast);
1753 }
1754 
updateHiddenSections(int logicalFirst,int logicalLast)1755 void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
1756 {
1757     Q_Q(QHeaderView);
1758     const int changeCount = logicalLast - logicalFirst + 1;
1759 
1760     // remove sections from hiddenSectionSize
1761     QHash<int, int> newHiddenSectionSize; // from logical index to section size
1762     for (int i = 0; i < logicalFirst; ++i)
1763         if (q->isSectionHidden(i))
1764             newHiddenSectionSize[i] = hiddenSectionSize[i];
1765     for (int j = logicalLast + 1; j < sectionCount; ++j)
1766         if (q->isSectionHidden(j))
1767             newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
1768     hiddenSectionSize = newHiddenSectionSize;
1769 
1770     // remove sections from sectionsHidden
1771     if (!sectionHidden.isEmpty()) {
1772         const int newsize = qMin(sectionCount - changeCount, sectionHidden.size());
1773         QBitArray newSectionHidden(newsize);
1774         for (int j = 0, k = 0; j < sectionHidden.size(); ++j) {
1775             const int logical = logicalIndex(j);
1776             if (logical < logicalFirst || logical > logicalLast) {
1777                 newSectionHidden[k++] = sectionHidden[j];
1778             }
1779         }
1780         sectionHidden = newSectionHidden;
1781     }
1782 }
1783 
_q_sectionsRemoved(const QModelIndex & parent,int logicalFirst,int logicalLast)1784 void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
1785                                             int logicalFirst, int logicalLast)
1786 {
1787     Q_Q(QHeaderView);
1788     if (parent != root)
1789         return; // we only handle changes in the top level
1790     if (qMin(logicalFirst, logicalLast) < 0
1791         || qMax(logicalLast, logicalFirst) >= sectionCount)
1792         return;
1793     int oldCount = q->count();
1794     int changeCount = logicalLast - logicalFirst + 1;
1795 
1796     updateHiddenSections(logicalFirst, logicalLast);
1797 
1798     if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
1799         //Q_ASSERT(headerSectionCount() == sectionCount);
1800         removeSectionsFromSpans(logicalFirst, logicalLast);
1801     } else {
1802         for (int l = logicalLast; l >= logicalFirst; --l) {
1803             int visual = visualIndices.at(l);
1804             for (int v = 0; v < sectionCount; ++v) {
1805                 if (v >= logicalIndices.count())
1806                     continue; // the section doesn't exist
1807                 if (v > visual) {
1808                     int logical = logicalIndices.at(v);
1809                     --(visualIndices[logical]);
1810                 }
1811                 if (logicalIndex(v) > l) // no need to move the positions before l
1812                     --(logicalIndices[v]);
1813             }
1814             logicalIndices.remove(visual);
1815             visualIndices.remove(l);
1816             //Q_ASSERT(headerSectionCount() == sectionCount);
1817             removeSectionsFromSpans(visual, visual);
1818         }
1819         // ### handle sectionSelection, sectionHidden
1820     }
1821     sectionCount -= changeCount;
1822 
1823     // update sorting column
1824     if (sortIndicatorSection >= logicalFirst) {
1825         if (sortIndicatorSection <= logicalLast)
1826             sortIndicatorSection = -1;
1827         else
1828             sortIndicatorSection -= changeCount;
1829     }
1830 
1831     // if we only have the last section (the "end" position) left, the header is empty
1832     if (sectionCount <= 0)
1833         clear();
1834     invalidateCachedSizeHint();
1835     emit q->sectionCountChanged(oldCount, q->count());
1836     viewport->update();
1837 }
1838 
_q_layoutAboutToBeChanged()1839 void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
1840 {
1841     //if there is no row/column we can't have mapping for columns
1842     //because no QModelIndex in the model would be valid
1843     // ### this is far from being bullet-proof and we would need a real system to
1844     // ### map columns or rows persistently
1845     if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
1846         || model->columnCount(root) == 0)
1847         return;
1848 
1849     for (int i = 0; i < sectionHidden.count(); ++i)
1850         if (sectionHidden.testBit(i)) // ### note that we are using column or row 0
1851             persistentHiddenSections.append(orientation == Qt::Horizontal
1852                                             ? model->index(0, logicalIndex(i), root)
1853                                             : model->index(logicalIndex(i), 0, root));
1854 }
1855 
_q_layoutChanged()1856 void QHeaderViewPrivate::_q_layoutChanged()
1857 {
1858     Q_Q(QHeaderView);
1859     viewport->update();
1860     if (persistentHiddenSections.isEmpty() || modelIsEmpty()) {
1861         if (modelSectionCount() != sectionCount)
1862             q->initializeSections();
1863         persistentHiddenSections.clear();
1864         return;
1865     }
1866 
1867     QBitArray oldSectionHidden = sectionHidden;
1868     bool sectionCountChanged = false;
1869 
1870     for (int i = 0; i < persistentHiddenSections.count(); ++i) {
1871         QModelIndex index = persistentHiddenSections.at(i);
1872         if (index.isValid()) {
1873             const int logical = (orientation == Qt::Horizontal
1874                                  ? index.column()
1875                                  : index.row());
1876             q->setSectionHidden(logical, true);
1877             oldSectionHidden.setBit(logical, false);
1878         } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) {
1879             sectionCountChanged = true;
1880             break;
1881         }
1882     }
1883     persistentHiddenSections.clear();
1884 
1885     for (int i = 0; i < oldSectionHidden.count(); ++i) {
1886         if (oldSectionHidden.testBit(i))
1887             q->setSectionHidden(i, false);
1888     }
1889 
1890     // the number of sections changed; we need to reread the state of the model
1891     if (sectionCountChanged)
1892         q->initializeSections();
1893 }
1894 
1895 /*!
1896   \internal
1897 */
1898 
initializeSections()1899 void QHeaderView::initializeSections()
1900 {
1901     Q_D(QHeaderView);
1902     const int oldCount = d->sectionCount;
1903     const int newCount = d->modelSectionCount();
1904     if (newCount <= 0) {
1905             d->clear();
1906             emit sectionCountChanged(oldCount, 0);
1907     } else if (newCount != oldCount) {
1908         const int min = qBound(0, oldCount, newCount - 1);
1909         initializeSections(min, newCount - 1);
1910         if (stretchLastSection()) // we've already gotten the size hint
1911             d->lastSectionSize = sectionSize(logicalIndex(d->sectionCount - 1));
1912 
1913         //make sure we update the hidden sections
1914         if (newCount < oldCount)
1915             d->updateHiddenSections(0, newCount-1);
1916     }
1917 }
1918 
1919 /*!
1920     \internal
1921 */
1922 
initializeSections(int start,int end)1923 void QHeaderView::initializeSections(int start, int end)
1924 {
1925     Q_D(QHeaderView);
1926 
1927     Q_ASSERT(start >= 0);
1928     Q_ASSERT(end >= 0);
1929 
1930     d->invalidateCachedSizeHint();
1931 
1932     if (end + 1 < d->sectionCount) {
1933         int newCount = end + 1;
1934         d->removeSectionsFromSpans(newCount, d->sectionCount);
1935         if (!d->hiddenSectionSize.isEmpty()) {
1936             if (d->sectionCount - newCount > d->hiddenSectionSize.count()) {
1937                 for (int i = end + 1; i < d->sectionCount; ++i)
1938                     d->hiddenSectionSize.remove(i);
1939             } else {
1940                 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
1941                 while (it != d->hiddenSectionSize.end()) {
1942                     if (it.key() > end)
1943                         it = d->hiddenSectionSize.erase(it);
1944                     else
1945                         ++it;
1946                 }
1947             }
1948         }
1949     }
1950 
1951     int oldCount = d->sectionCount;
1952     d->sectionCount = end + 1;
1953 
1954     if (!d->logicalIndices.isEmpty()) {
1955         if (oldCount <= d->sectionCount) {
1956             d->logicalIndices.resize(d->sectionCount);
1957             d->visualIndices.resize(d->sectionCount);
1958             for (int i = oldCount; i < d->sectionCount; ++i) {
1959                 d->logicalIndices[i] = i;
1960                 d->visualIndices[i] = i;
1961             }
1962         } else {
1963             int j = 0;
1964             for (int i = 0; i < oldCount; ++i) {
1965                 int v = d->logicalIndices.at(i);
1966                 if (v < d->sectionCount) {
1967                     d->logicalIndices[j] = v;
1968                     d->visualIndices[v] = j;
1969                     j++;
1970                 }
1971             }
1972             d->logicalIndices.resize(d->sectionCount);
1973             d->visualIndices.resize(d->sectionCount);
1974         }
1975     }
1976 
1977     if (d->globalResizeMode == Stretch)
1978         d->stretchSections = d->sectionCount;
1979     else if (d->globalResizeMode == ResizeToContents)
1980          d->contentsSections = d->sectionCount;
1981     if (!d->sectionHidden.isEmpty())
1982         d->sectionHidden.resize(d->sectionCount);
1983 
1984     if (d->sectionCount > oldCount)
1985         d->createSectionSpan(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
1986     //Q_ASSERT(d->headerLength() == d->length);
1987 
1988     if (d->sectionCount != oldCount)
1989         emit sectionCountChanged(oldCount,  d->sectionCount);
1990     d->viewport->update();
1991 }
1992 
1993 /*!
1994   \reimp
1995 */
1996 
currentChanged(const QModelIndex & current,const QModelIndex & old)1997 void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
1998 {
1999     Q_D(QHeaderView);
2000 
2001     if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
2002         if (old.isValid() && old.parent() == d->root)
2003             d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
2004                                     sectionSize(old.column()), d->viewport->height()));
2005         if (current.isValid() && current.parent() == d->root)
2006             d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
2007                                     sectionSize(current.column()), d->viewport->height()));
2008     } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
2009         if (old.isValid() && old.parent() == d->root)
2010             d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
2011                                     d->viewport->width(), sectionSize(old.row())));
2012         if (current.isValid() && current.parent() == d->root)
2013             d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2014                                     d->viewport->width(), sectionSize(current.row())));
2015     }
2016 }
2017 
2018 
2019 /*!
2020   \reimp
2021 */
2022 
event(QEvent * e)2023 bool QHeaderView::event(QEvent *e)
2024 {
2025     Q_D(QHeaderView);
2026     switch (e->type()) {
2027     case QEvent::HoverEnter: {
2028         QHoverEvent *he = static_cast<QHoverEvent*>(e);
2029         d->hover = logicalIndexAt(he->pos());
2030         if (d->hover != -1)
2031             updateSection(d->hover);
2032         break; }
2033     case QEvent::Leave:
2034     case QEvent::HoverLeave: {
2035         if (d->hover != -1)
2036             updateSection(d->hover);
2037         d->hover = -1;
2038         break; }
2039     case QEvent::HoverMove: {
2040         QHoverEvent *he = static_cast<QHoverEvent*>(e);
2041         int oldHover = d->hover;
2042         d->hover = logicalIndexAt(he->pos());
2043         if (d->hover != oldHover) {
2044             if (oldHover != -1)
2045                 updateSection(oldHover);
2046             if (d->hover != -1)
2047                 updateSection(d->hover);
2048         }
2049         break; }
2050     case QEvent::Timer: {
2051         QTimerEvent *te = static_cast<QTimerEvent*>(e);
2052         if (te->timerId() == d->delayedResize.timerId()) {
2053             d->delayedResize.stop();
2054             resizeSections();
2055         }
2056         break; }
2057     default:
2058         break;
2059     }
2060     return QAbstractItemView::event(e);
2061 }
2062 
2063 /*!
2064   \reimp
2065 */
2066 
paintEvent(QPaintEvent * e)2067 void QHeaderView::paintEvent(QPaintEvent *e)
2068 {
2069     Q_D(QHeaderView);
2070 
2071     if (count() == 0)
2072         return;
2073 
2074     QPainter painter(d->viewport);
2075     const QPoint offset = d->scrollDelayOffset;
2076     QRect translatedEventRect = e->rect();
2077     translatedEventRect.translate(offset);
2078 
2079     int start = -1;
2080     int end = -1;
2081     if (d->orientation == Qt::Horizontal) {
2082         start = visualIndexAt(translatedEventRect.left());
2083         end = visualIndexAt(translatedEventRect.right());
2084     } else {
2085         start = visualIndexAt(translatedEventRect.top());
2086         end = visualIndexAt(translatedEventRect.bottom());
2087     }
2088 
2089     if (d->reverse()) {
2090         start = (start == -1 ? count() - 1 : start);
2091         end = (end == -1 ? 0 : end);
2092     } else {
2093         start = (start == -1 ? 0 : start);
2094         end = (end == -1 ? count() - 1 : end);
2095     }
2096 
2097     int tmp = start;
2098     start = qMin(start, end);
2099     end = qMax(tmp, end);
2100 
2101     d->prepareSectionSelected(); // clear and resize the bit array
2102 
2103     QRect currentSectionRect;
2104     int logical;
2105     const int width = d->viewport->width();
2106     const int height = d->viewport->height();
2107     for (int i = start; i <= end; ++i) {
2108         if (d->isVisualIndexHidden(i))
2109             continue;
2110         painter.save();
2111         logical = logicalIndex(i);
2112         if (d->orientation == Qt::Horizontal) {
2113             currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height);
2114         } else {
2115             currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical));
2116         }
2117         currentSectionRect.translate(offset);
2118 
2119         QVariant variant = d->model->headerData(logical, d->orientation,
2120                                                 Qt::FontRole);
2121         if (variant.isValid() && variant.canConvert<QFont>()) {
2122             QFont sectionFont = qvariant_cast<QFont>(variant);
2123             painter.setFont(sectionFont);
2124         }
2125         paintSection(&painter, currentSectionRect, logical);
2126         painter.restore();
2127     }
2128 
2129     QStyleOption opt;
2130     opt.init(this);
2131     // Paint the area beyond where there are indexes
2132     if (d->reverse()) {
2133         opt.state |= QStyle::State_Horizontal;
2134         if (currentSectionRect.left() > translatedEventRect.left()) {
2135             opt.rect = QRect(translatedEventRect.left(), 0,
2136                              currentSectionRect.left() - translatedEventRect.left(), height);
2137             style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2138         }
2139     } else if (currentSectionRect.right() < translatedEventRect.right()) {
2140         // paint to the right
2141         opt.state |= QStyle::State_Horizontal;
2142         opt.rect = QRect(currentSectionRect.right() + 1, 0,
2143                          translatedEventRect.right() - currentSectionRect.right(), height);
2144         style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2145     } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2146         // paint the bottom section
2147         opt.state &= ~QStyle::State_Horizontal;
2148         opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2149                          width, height - currentSectionRect.bottom() - 1);
2150         style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2151     }
2152 
2153 #if 0
2154     // ### visualize section spans
2155     for (int a = 0, i = 0; i < d->sectionSpans.count(); ++i) {
2156         QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2157         if (d->orientation == Qt::Horizontal)
2158             painter.fillRect(a - d->offset, 0, d->sectionSpans.at(i).size, 4, color);
2159         else
2160             painter.fillRect(0, a - d->offset, 4, d->sectionSpans.at(i).size, color);
2161         a += d->sectionSpans.at(i).size;
2162     }
2163 
2164 #endif
2165 }
2166 
2167 /*!
2168   \reimp
2169 */
2170 
mousePressEvent(QMouseEvent * e)2171 void QHeaderView::mousePressEvent(QMouseEvent *e)
2172 {
2173     Q_D(QHeaderView);
2174     if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2175         return;
2176     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2177     int handle = d->sectionHandleAt(pos);
2178     d->originalSize = -1; // clear the stored original size
2179     if (handle == -1) {
2180         d->pressed = logicalIndexAt(pos);
2181         if (d->clickableSections)
2182             emit sectionPressed(d->pressed);
2183         if (d->movableSections) {
2184             d->section = d->target = d->pressed;
2185             if (d->section == -1)
2186                 return;
2187             d->state = QHeaderViewPrivate::MoveSection;
2188             d->setupSectionIndicator(d->section, pos);
2189         } else if (d->clickableSections && d->pressed != -1) {
2190             updateSection(d->pressed);
2191             d->state = QHeaderViewPrivate::SelectSections;
2192         }
2193     } else if (resizeMode(handle) == Interactive) {
2194         d->originalSize = sectionSize(handle);
2195         d->state = QHeaderViewPrivate::ResizeSection;
2196         d->section = handle;
2197     }
2198 
2199     d->firstPos = pos;
2200     d->lastPos = pos;
2201 
2202     d->clearCascadingSections();
2203 }
2204 
2205 /*!
2206   \reimp
2207 */
2208 
mouseMoveEvent(QMouseEvent * e)2209 void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2210 {
2211     Q_D(QHeaderView);
2212     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2213     if (pos < 0 && d->state != QHeaderViewPrivate::SelectSections)
2214         return;
2215     if (e->buttons() == Qt::NoButton) {
2216 #if !defined(Q_WS_MAC)
2217         // Under Cocoa, when the mouse button is released, may include an extra
2218         // simulated mouse moved event. The state of the buttons when this event
2219         // is generated is already "no button" and the code below gets executed
2220         // just before the mouseReleaseEvent and resets the state. This prevents
2221         // column dragging from working. So this code is disabled under Cocoa.
2222         d->state = QHeaderViewPrivate::NoState;
2223         d->pressed = -1;
2224 #endif
2225     }
2226     switch (d->state) {
2227         case QHeaderViewPrivate::ResizeSection: {
2228             Q_ASSERT(d->originalSize != -1);
2229             if (d->cascadingResizing) {
2230                 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2231                 int visual = visualIndex(d->section);
2232                 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2233             } else {
2234                 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2235                 resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize()));
2236             }
2237             d->lastPos = pos;
2238             return;
2239         }
2240         case QHeaderViewPrivate::MoveSection: {
2241             if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2242                 || !d->sectionIndicator->isHidden()) {
2243                 int visual = visualIndexAt(pos);
2244                 if (visual == -1)
2245                     return;
2246                 int posThreshold = d->headerSectionPosition(visual) - d->offset + d->headerSectionSize(visual) / 2;
2247                 int moving = visualIndex(d->section);
2248                 if (visual < moving) {
2249                     if (pos < posThreshold)
2250                         d->target = d->logicalIndex(visual);
2251                     else
2252                         d->target = d->logicalIndex(visual + 1);
2253                 } else if (visual > moving) {
2254                     if (pos > posThreshold)
2255                         d->target = d->logicalIndex(visual);
2256                     else
2257                         d->target = d->logicalIndex(visual - 1);
2258                 } else {
2259                     d->target = d->section;
2260                 }
2261                 d->updateSectionIndicator(d->section, pos);
2262             }
2263             return;
2264         }
2265         case QHeaderViewPrivate::SelectSections: {
2266             int logical = logicalIndexAt(qMax(-d->offset, pos));
2267             if (logical == -1 && pos > 0)
2268                 logical = d->lastVisibleVisualIndex();
2269             if (logical == d->pressed)
2270                 return; // nothing to do
2271             else if (d->pressed != -1)
2272                 updateSection(d->pressed);
2273             d->pressed = logical;
2274             if (d->clickableSections && logical != -1) {
2275                 emit sectionEntered(d->pressed);
2276                 updateSection(d->pressed);
2277             }
2278             return;
2279         }
2280         case QHeaderViewPrivate::NoState: {
2281 #ifndef QT_NO_CURSOR
2282             int handle = d->sectionHandleAt(pos);
2283             bool hasCursor = testAttribute(Qt::WA_SetCursor);
2284             if (handle != -1 && (resizeMode(handle) == Interactive)) {
2285                 if (!hasCursor)
2286                     setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2287             } else if (hasCursor) {
2288                 unsetCursor();
2289             }
2290 #endif
2291             return;
2292         }
2293         default:
2294             break;
2295     }
2296 }
2297 
2298 /*!
2299   \reimp
2300 */
2301 
mouseReleaseEvent(QMouseEvent * e)2302 void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2303 {
2304     Q_D(QHeaderView);
2305     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2306     switch (d->state) {
2307     case QHeaderViewPrivate::MoveSection:
2308         if (!d->sectionIndicator->isHidden()) { // moving
2309             int from = visualIndex(d->section);
2310             Q_ASSERT(from != -1);
2311             int to = visualIndex(d->target);
2312             Q_ASSERT(to != -1);
2313             moveSection(from, to);
2314             d->section = d->target = -1;
2315             d->updateSectionIndicator(d->section, pos);
2316             break;
2317         } // not moving
2318     case QHeaderViewPrivate::SelectSections:
2319         if (!d->clickableSections) {
2320             int section = logicalIndexAt(pos);
2321             updateSection(section);
2322         }
2323         // fall through
2324     case QHeaderViewPrivate::NoState:
2325         if (d->clickableSections) {
2326             int section = logicalIndexAt(pos);
2327             if (section != -1 && section == d->pressed) {
2328                 d->flipSortIndicator(section);
2329                 emit sectionClicked(section);
2330             }
2331             if (d->pressed != -1)
2332                 updateSection(d->pressed);
2333         }
2334         break;
2335     case QHeaderViewPrivate::ResizeSection:
2336         d->originalSize = -1;
2337         d->clearCascadingSections();
2338         break;
2339     default:
2340         break;
2341     }
2342     d->state = QHeaderViewPrivate::NoState;
2343     d->pressed = -1;
2344 }
2345 
2346 /*!
2347   \reimp
2348 */
2349 
mouseDoubleClickEvent(QMouseEvent * e)2350 void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2351 {
2352     Q_D(QHeaderView);
2353     int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2354     int handle = d->sectionHandleAt(pos);
2355     if (handle > -1 && resizeMode(handle) == Interactive) {
2356         emit sectionHandleDoubleClicked(handle);
2357 #ifndef QT_NO_CURSOR
2358         Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2359                                       ? Qt::SplitHCursor : Qt::SplitVCursor;
2360         if (cursor().shape() == splitCursor) {
2361             // signal handlers may have changed the section size
2362             handle = d->sectionHandleAt(pos);
2363             if (!(handle > -1 && resizeMode(handle) == Interactive))
2364                 setCursor(Qt::ArrowCursor);
2365         }
2366 #endif
2367     } else {
2368         emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2369     }
2370 }
2371 
2372 /*!
2373   \reimp
2374 */
2375 
viewportEvent(QEvent * e)2376 bool QHeaderView::viewportEvent(QEvent *e)
2377 {
2378     Q_D(QHeaderView);
2379     switch (e->type()) {
2380 #ifndef QT_NO_TOOLTIP
2381     case QEvent::ToolTip: {
2382         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2383         int logical = logicalIndexAt(he->pos());
2384         if (logical != -1) {
2385             QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2386             if (variant.isValid()) {
2387                 QToolTip::showText(he->globalPos(), variant.toString(), this);
2388                 return true;
2389             }
2390         }
2391         break; }
2392 #endif
2393 #ifndef QT_NO_WHATSTHIS
2394     case QEvent::QueryWhatsThis: {
2395         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2396         int logical = logicalIndexAt(he->pos());
2397         if (logical != -1
2398             && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2399             return true;
2400         break; }
2401     case QEvent::WhatsThis: {
2402         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2403         int logical = logicalIndexAt(he->pos());
2404         if (logical != -1) {
2405              QVariant whatsthis = d->model->headerData(logical, d->orientation,
2406                                                       Qt::WhatsThisRole);
2407              if (whatsthis.isValid()) {
2408                  QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2409                  return true;
2410              }
2411         }
2412         break; }
2413 #endif // QT_NO_WHATSTHIS
2414 #ifndef QT_NO_STATUSTIP
2415     case QEvent::StatusTip: {
2416         QHelpEvent *he = static_cast<QHelpEvent*>(e);
2417         int logical = logicalIndexAt(he->pos());
2418         if (logical != -1) {
2419             QString statustip = d->model->headerData(logical, d->orientation,
2420                                                     Qt::StatusTipRole).toString();
2421             if (!statustip.isEmpty())
2422                 setStatusTip(statustip);
2423         }
2424         return true; }
2425 #endif // QT_NO_STATUSTIP
2426     case QEvent::Hide:
2427     case QEvent::Show:
2428     case QEvent::FontChange:
2429     case QEvent::StyleChange:
2430         d->invalidateCachedSizeHint();
2431         resizeSections();
2432         emit geometriesChanged();
2433         break;
2434     case QEvent::ContextMenu: {
2435         d->state = QHeaderViewPrivate::NoState;
2436         d->pressed = d->section = d->target = -1;
2437         d->updateSectionIndicator(d->section, -1);
2438         break; }
2439     case QEvent::Wheel: {
2440         QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2441         if (asa)
2442             return QApplication::sendEvent(asa->viewport(), e);
2443         break; }
2444     default:
2445         break;
2446     }
2447     return QAbstractItemView::viewportEvent(e);
2448 }
2449 
2450 /*!
2451     Paints the section specified by the given \a logicalIndex, using the given
2452     \a painter and \a rect.
2453 
2454     Normally, you do not have to call this function.
2455 */
2456 
paintSection(QPainter * painter,const QRect & rect,int logicalIndex) const2457 void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2458 {
2459     Q_D(const QHeaderView);
2460     if (!rect.isValid())
2461         return;
2462     // get the state of the section
2463     QStyleOptionHeader opt;
2464     initStyleOption(&opt);
2465     QStyle::State state = QStyle::State_None;
2466     if (isEnabled())
2467         state |= QStyle::State_Enabled;
2468     if (window()->isActiveWindow())
2469         state |= QStyle::State_Active;
2470     if (d->clickableSections) {
2471         if (logicalIndex == d->hover)
2472             state |= QStyle::State_MouseOver;
2473         if (logicalIndex == d->pressed)
2474             state |= QStyle::State_Sunken;
2475         else if (d->highlightSelected) {
2476             if (d->sectionIntersectsSelection(logicalIndex))
2477                 state |= QStyle::State_On;
2478             if (d->isSectionSelected(logicalIndex))
2479                 state |= QStyle::State_Sunken;
2480         }
2481 
2482     }
2483     if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2484         opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2485                             ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2486 
2487     // setup the style options structure
2488     QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2489                                                   Qt::TextAlignmentRole);
2490     opt.rect = rect;
2491     opt.section = logicalIndex;
2492     opt.state |= state;
2493     opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2494                                       ? Qt::Alignment(textAlignment.toInt())
2495                                       : d->defaultAlignment);
2496 
2497     opt.iconAlignment = Qt::AlignVCenter;
2498     opt.text = d->model->headerData(logicalIndex, d->orientation,
2499                                     Qt::DisplayRole).toString();
2500     if (d->textElideMode != Qt::ElideNone)
2501         opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);
2502 
2503     QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2504                                     Qt::DecorationRole);
2505     opt.icon = qvariant_cast<QIcon>(variant);
2506     if (opt.icon.isNull())
2507         opt.icon = qvariant_cast<QPixmap>(variant);
2508     QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2509                                                     Qt::ForegroundRole);
2510     if (foregroundBrush.canConvert<QBrush>())
2511         opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2512 
2513     QPointF oldBO = painter->brushOrigin();
2514     QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2515                                                     Qt::BackgroundRole);
2516     if (backgroundBrush.canConvert<QBrush>()) {
2517         opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2518         opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2519         painter->setBrushOrigin(opt.rect.topLeft());
2520     }
2521 
2522     // the section position
2523     int visual = visualIndex(logicalIndex);
2524     Q_ASSERT(visual != -1);
2525     if (count() == 1)
2526         opt.position = QStyleOptionHeader::OnlyOneSection;
2527     else if (visual == 0)
2528         opt.position = QStyleOptionHeader::Beginning;
2529     else if (visual == count() - 1)
2530         opt.position = QStyleOptionHeader::End;
2531     else
2532         opt.position = QStyleOptionHeader::Middle;
2533     opt.orientation = d->orientation;
2534     // the selected position
2535     bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
2536     bool nextSelected =  d->isSectionSelected(this->logicalIndex(visual + 1));
2537     if (previousSelected && nextSelected)
2538         opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
2539     else if (previousSelected)
2540         opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
2541     else if (nextSelected)
2542         opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
2543     else
2544         opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
2545     // draw the section
2546     style()->drawControl(QStyle::CE_Header, &opt, painter, this);
2547 
2548     painter->setBrushOrigin(oldBO);
2549 }
2550 
2551 /*!
2552     Returns the size of the contents of the section specified by the given
2553     \a logicalIndex.
2554 
2555     \sa defaultSectionSize()
2556 */
2557 
sectionSizeFromContents(int logicalIndex) const2558 QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
2559 {
2560     Q_D(const QHeaderView);
2561     Q_ASSERT(logicalIndex >= 0);
2562 
2563     ensurePolished();
2564 
2565     // use SizeHintRole
2566     QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
2567     if (variant.isValid())
2568         return qvariant_cast<QSize>(variant);
2569 
2570     // otherwise use the contents
2571     QStyleOptionHeader opt;
2572     initStyleOption(&opt);
2573     opt.section = logicalIndex;
2574     QVariant var = d->model->headerData(logicalIndex, d->orientation,
2575                                             Qt::FontRole);
2576     QFont fnt;
2577     if (var.isValid() && var.canConvert<QFont>())
2578         fnt = qvariant_cast<QFont>(var);
2579     else
2580         fnt = font();
2581     fnt.setBold(true);
2582     opt.fontMetrics = QFontMetrics(fnt);
2583     opt.text = d->model->headerData(logicalIndex, d->orientation,
2584                                     Qt::DisplayRole).toString();
2585     variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
2586     opt.icon = qvariant_cast<QIcon>(variant);
2587     if (opt.icon.isNull())
2588         opt.icon = qvariant_cast<QPixmap>(variant);
2589     QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
2590     if (isSortIndicatorShown()) {
2591         int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, &opt, this);
2592         if (d->orientation == Qt::Horizontal)
2593             size.rwidth() += size.height() + margin;
2594         else
2595             size.rheight() += size.width() + margin;
2596     }
2597     return size;
2598 }
2599 
2600 /*!
2601     Returns the horizontal offset of the header. This is 0 for vertical
2602     headers.
2603 
2604     \sa offset()
2605 */
2606 
horizontalOffset() const2607 int QHeaderView::horizontalOffset() const
2608 {
2609     Q_D(const QHeaderView);
2610     if (d->orientation == Qt::Horizontal)
2611         return d->offset;
2612     return 0;
2613 }
2614 
2615 /*!
2616     Returns the vertical offset of the header. This is 0 for horizontal
2617     headers.
2618 
2619     \sa offset()
2620 */
2621 
verticalOffset() const2622 int QHeaderView::verticalOffset() const
2623 {
2624     Q_D(const QHeaderView);
2625     if (d->orientation == Qt::Vertical)
2626         return d->offset;
2627     return 0;
2628 }
2629 
2630 /*!
2631     \reimp
2632     \internal
2633 */
2634 
updateGeometries()2635 void QHeaderView::updateGeometries()
2636 {
2637     Q_D(QHeaderView);
2638     d->layoutChildren();
2639     if (d->hasAutoResizeSections())
2640         d->doDelayedResizeSections();
2641 }
2642 
2643 /*!
2644     \reimp
2645     \internal
2646 */
2647 
scrollContentsBy(int dx,int dy)2648 void QHeaderView::scrollContentsBy(int dx, int dy)
2649 {
2650     Q_D(QHeaderView);
2651     d->scrollDirtyRegion(dx, dy);
2652 }
2653 
2654 /*!
2655     \reimp
2656     \internal
2657 */
dataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)2658 void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
2659 {
2660     Q_D(QHeaderView);
2661     d->invalidateCachedSizeHint();
2662     if (d->hasAutoResizeSections()) {
2663         bool resizeRequired = d->globalResizeMode == ResizeToContents;
2664         int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
2665         int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
2666         for (int i = first; i <= last && !resizeRequired; ++i)
2667             resizeRequired = (resizeMode(i) == ResizeToContents);
2668         if (resizeRequired)
2669             d->doDelayedResizeSections();
2670     }
2671 }
2672 
2673 /*!
2674     \reimp
2675     \internal
2676 
2677     Empty implementation because the header doesn't show QModelIndex items.
2678 */
rowsInserted(const QModelIndex &,int,int)2679 void QHeaderView::rowsInserted(const QModelIndex &, int, int)
2680 {
2681     // do nothing
2682 }
2683 
2684 /*!
2685     \reimp
2686     \internal
2687 
2688     Empty implementation because the header doesn't show QModelIndex items.
2689 */
2690 
visualRect(const QModelIndex &) const2691 QRect QHeaderView::visualRect(const QModelIndex &) const
2692 {
2693     return QRect();
2694 }
2695 
2696 /*!
2697     \reimp
2698     \internal
2699 
2700     Empty implementation because the header doesn't show QModelIndex items.
2701 */
2702 
scrollTo(const QModelIndex &,ScrollHint)2703 void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
2704 {
2705     // do nothing - the header only displays sections
2706 }
2707 
2708 /*!
2709     \reimp
2710     \internal
2711 
2712     Empty implementation because the header doesn't show QModelIndex items.
2713 */
2714 
indexAt(const QPoint &) const2715 QModelIndex QHeaderView::indexAt(const QPoint &) const
2716 {
2717     return QModelIndex();
2718 }
2719 
2720 /*!
2721     \reimp
2722     \internal
2723 
2724     Empty implementation because the header doesn't show QModelIndex items.
2725 */
2726 
isIndexHidden(const QModelIndex &) const2727 bool QHeaderView::isIndexHidden(const QModelIndex &) const
2728 {
2729     return true; // the header view has no items, just sections
2730 }
2731 
2732 /*!
2733     \reimp
2734     \internal
2735 
2736     Empty implementation because the header doesn't show QModelIndex items.
2737 */
2738 
moveCursor(CursorAction,Qt::KeyboardModifiers)2739 QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
2740 {
2741     return QModelIndex();
2742 }
2743 
2744 /*!
2745     \reimp
2746 
2747     Selects the items in the given \a rect according to the specified
2748     \a flags.
2749 
2750     The base class implementation does nothing.
2751 */
2752 
setSelection(const QRect &,QItemSelectionModel::SelectionFlags)2753 void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
2754 {
2755     // do nothing
2756 }
2757 
2758 /*!
2759     \internal
2760 */
2761 
visualRegionForSelection(const QItemSelection & selection) const2762 QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
2763 {
2764     Q_D(const QHeaderView);
2765     const int max = d->modelSectionCount();
2766     if (d->orientation == Qt::Horizontal) {
2767         int left = max;
2768         int right = 0;
2769         int rangeLeft, rangeRight;
2770 
2771         for (int i = 0; i < selection.count(); ++i) {
2772             QItemSelectionRange r = selection.at(i);
2773             if (r.parent().isValid() || !r.isValid())
2774                 continue; // we only know about toplevel items and we don't want invalid ranges
2775             // FIXME an item inside the range may be the leftmost or rightmost
2776             rangeLeft = visualIndex(r.left());
2777             if (rangeLeft == -1) // in some cases users may change the selections
2778                 continue;        // before we have a chance to do the layout
2779             rangeRight = visualIndex(r.right());
2780             if (rangeRight == -1) // in some cases users may change the selections
2781                 continue;         // before we have a chance to do the layout
2782             if (rangeLeft < left)
2783                 left = rangeLeft;
2784             if (rangeRight > right)
2785                 right = rangeRight;
2786         }
2787 
2788         int logicalLeft = logicalIndex(left);
2789         int logicalRight = logicalIndex(right);
2790 
2791         if (logicalLeft < 0  || logicalLeft >= count() ||
2792             logicalRight < 0 || logicalRight >= count())
2793             return QRegion();
2794 
2795         int leftPos = sectionViewportPosition(logicalLeft);
2796         int rightPos = sectionViewportPosition(logicalRight);
2797         rightPos += sectionSize(logicalRight);
2798         return QRect(leftPos, 0, rightPos - leftPos, height());
2799     }
2800     // orientation() == Qt::Vertical
2801     int top = max;
2802     int bottom = 0;
2803     int rangeTop, rangeBottom;
2804 
2805     for (int i = 0; i < selection.count(); ++i) {
2806         QItemSelectionRange r = selection.at(i);
2807         if (r.parent().isValid() || !r.isValid())
2808             continue; // we only know about toplevel items
2809         // FIXME an item inside the range may be the leftmost or rightmost
2810         rangeTop = visualIndex(r.top());
2811         if (rangeTop == -1) // in some cases users may change the selections
2812             continue;       // before we have a chance to do the layout
2813         rangeBottom = visualIndex(r.bottom());
2814         if (rangeBottom == -1) // in some cases users may change the selections
2815             continue;          // before we have a chance to do the layout
2816         if (rangeTop < top)
2817             top = rangeTop;
2818         if (rangeBottom > bottom)
2819             bottom = rangeBottom;
2820     }
2821 
2822     int logicalTop = logicalIndex(top);
2823     int logicalBottom = logicalIndex(bottom);
2824 
2825     if (logicalTop == -1 || logicalBottom == -1)
2826         return QRect();
2827 
2828     int topPos = sectionViewportPosition(logicalTop);
2829     int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
2830 
2831     return QRect(0, topPos, width(), bottomPos - topPos);
2832 }
2833 
2834 
2835 // private implementation
2836 
sectionHandleAt(int position)2837 int QHeaderViewPrivate::sectionHandleAt(int position)
2838 {
2839     Q_Q(QHeaderView);
2840     int visual = q->visualIndexAt(position);
2841     if (visual == -1)
2842         return -1;
2843     int log = logicalIndex(visual);
2844     int pos = q->sectionViewportPosition(log);
2845     int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, q);
2846 
2847     bool atLeft = position < pos + grip;
2848     bool atRight = (position > pos + q->sectionSize(log) - grip);
2849     if (reverse())
2850         qSwap(atLeft, atRight);
2851 
2852     if (atLeft) {
2853         //grip at the beginning of the section
2854         while(visual > -1) {
2855             int logical = q->logicalIndex(--visual);
2856             if (!q->isSectionHidden(logical))
2857                 return logical;
2858         }
2859     } else if (atRight) {
2860         //grip at the end of the section
2861         return log;
2862     }
2863     return -1;
2864 }
2865 
setupSectionIndicator(int section,int position)2866 void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
2867 {
2868     Q_Q(QHeaderView);
2869     if (!sectionIndicator) {
2870         sectionIndicator = new QLabel(viewport);
2871     }
2872 
2873     int w, h;
2874     int p = q->sectionViewportPosition(section);
2875     if (orientation == Qt::Horizontal) {
2876         w = q->sectionSize(section);
2877         h = viewport->height();
2878     } else {
2879         w = viewport->width();
2880         h = q->sectionSize(section);
2881     }
2882     sectionIndicator->resize(w, h);
2883 
2884     QPixmap pm(w, h);
2885     pm.fill(QColor(0, 0, 0, 45));
2886     QRect rect(0, 0, w, h);
2887 
2888     QPainter painter(&pm);
2889     painter.setOpacity(0.75);
2890     q->paintSection(&painter, rect, section);
2891     painter.end();
2892 
2893     sectionIndicator->setPixmap(pm);
2894     sectionIndicatorOffset = position - qMax(p, 0);
2895 }
2896 
updateSectionIndicator(int section,int position)2897 void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
2898 {
2899     if (!sectionIndicator)
2900         return;
2901 
2902     if (section == -1 || target == -1) {
2903         sectionIndicator->hide();
2904         return;
2905     }
2906 
2907     if (orientation == Qt::Horizontal)
2908         sectionIndicator->move(position - sectionIndicatorOffset, 0);
2909     else
2910         sectionIndicator->move(0, position - sectionIndicatorOffset);
2911 
2912     sectionIndicator->show();
2913 }
2914 
2915 /*!
2916     Initialize \a option with the values from this QHeaderView. This method is
2917     useful for subclasses when they need a QStyleOptionHeader, but do not want
2918     to fill in all the information themselves.
2919 
2920     \sa QStyleOption::initFrom()
2921 */
initStyleOption(QStyleOptionHeader * option) const2922 void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
2923 {
2924     Q_D(const QHeaderView);
2925     option->initFrom(this);
2926     option->state = QStyle::State_None | QStyle::State_Raised;
2927     option->orientation = d->orientation;
2928     if (d->orientation == Qt::Horizontal)
2929         option->state |= QStyle::State_Horizontal;
2930     if (isEnabled())
2931         option->state |= QStyle::State_Enabled;
2932     option->section = 0;
2933 }
2934 
isSectionSelected(int section) const2935 bool QHeaderViewPrivate::isSectionSelected(int section) const
2936 {
2937     int i = section * 2;
2938     if (i < 0 || i >= sectionSelected.count())
2939         return false;
2940     if (sectionSelected.testBit(i)) // if the value was cached
2941         return sectionSelected.testBit(i + 1);
2942     bool s = false;
2943     if (orientation == Qt::Horizontal)
2944         s = isColumnSelected(section);
2945     else
2946         s = isRowSelected(section);
2947     sectionSelected.setBit(i + 1, s); // selection state
2948     sectionSelected.setBit(i, true); // cache state
2949     return s;
2950 }
2951 
2952 /*!
2953     \internal
2954     Returns the last visible (ie. not hidden) visual index
2955 */
lastVisibleVisualIndex() const2956 int QHeaderViewPrivate::lastVisibleVisualIndex() const
2957 {
2958     Q_Q(const QHeaderView);
2959     for (int visual = q->count()-1; visual >= 0; --visual) {
2960         if (!q->isSectionHidden(q->logicalIndex(visual)))
2961             return visual;
2962     }
2963 
2964     //default value if no section is actually visible
2965     return -1;
2966 }
2967 
2968 /*!
2969     \internal
2970     Go through and resize all of the sections applying stretchLastSection,
2971     manualy stretches, sizes, and useGlobalMode.
2972 
2973     The different resize modes are:
2974     Interactive - the user decides the size
2975     Stretch - take up whatever space is left
2976     Fixed - the size is set programmatically outside the header
2977     ResizeToContentes - the size is set based on the contents of the row or column in the parent view
2978 
2979     The resize mode will not affect the last section if stretchLastSection is true.
2980 */
resizeSections(QHeaderView::ResizeMode globalMode,bool useGlobalMode)2981 void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
2982 {
2983     Q_Q(QHeaderView);
2984     //stop the timer in case it is delayed
2985     delayedResize.stop();
2986 
2987     executePostedLayout();
2988     if (sectionCount == 0)
2989         return;
2990 
2991     if (resizeRecursionBlock)
2992         return;
2993     resizeRecursionBlock = true;
2994 
2995     invalidateCachedSizeHint();
2996 
2997     const int lastVisibleSection = lastVisibleVisualIndex();
2998 
2999     // find stretchLastSection if we have it
3000     int stretchSection = -1;
3001     if (stretchLastSection && !useGlobalMode)
3002         stretchSection = lastVisibleVisualIndex();
3003 
3004     // count up the number of strected sections and how much space left for them
3005     int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
3006     int numberOfStretchedSections = 0;
3007     QList<int> section_sizes;
3008     for (int i = 0; i < sectionCount; ++i) {
3009         if (isVisualIndexHidden(i))
3010             continue;
3011 
3012         QHeaderView::ResizeMode resizeMode;
3013         if (useGlobalMode && (i != stretchSection))
3014             resizeMode = globalMode;
3015         else
3016             resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i));
3017 
3018         if (resizeMode == QHeaderView::Stretch) {
3019             ++numberOfStretchedSections;
3020             section_sizes.append(headerSectionSize(i));
3021             continue;
3022         }
3023 
3024         // because it isn't stretch, determine its width and remove that from lengthToStrech
3025         int sectionSize = 0;
3026         if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
3027             sectionSize = headerSectionSize(i);
3028         } else { // resizeMode == QHeaderView::ResizeToContents
3029             int logicalIndex = q->logicalIndex(i);
3030             sectionSize = qMax(viewSectionSizeHint(logicalIndex),
3031                                q->sectionSizeHint(logicalIndex));
3032         }
3033         section_sizes.append(sectionSize);
3034         lengthToStrech -= sectionSize;
3035     }
3036 
3037     // calculate the new length for all of the stretched sections
3038     int stretchSectionLength = -1;
3039     int pixelReminder = 0;
3040     if (numberOfStretchedSections > 0 && lengthToStrech > 0) { // we have room to stretch in
3041         int hintLengthForEveryStretchedSection = lengthToStrech / numberOfStretchedSections;
3042         stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
3043         pixelReminder = lengthToStrech % numberOfStretchedSections;
3044     }
3045 
3046     int spanStartSection = 0;
3047     int previousSectionLength = 0;
3048 
3049     QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
3050 
3051     // resize each section along the total length
3052     for (int i = 0; i < sectionCount; ++i) {
3053         int oldSectionLength = headerSectionSize(i);
3054         int newSectionLength = -1;
3055         QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
3056 
3057         if (isVisualIndexHidden(i)) {
3058             newSectionLength = 0;
3059         } else {
3060             QHeaderView::ResizeMode resizeMode;
3061             if (useGlobalMode)
3062                 resizeMode = globalMode;
3063             else
3064                 resizeMode = (i == stretchSection
3065                               ? QHeaderView::Stretch
3066                               : newSectionResizeMode);
3067             if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
3068                 if (i == lastVisibleSection)
3069                     newSectionLength = qMax(stretchSectionLength, lastSectionSize);
3070                 else
3071                     newSectionLength = stretchSectionLength;
3072                 if (pixelReminder > 0) {
3073                     newSectionLength += 1;
3074                     --pixelReminder;
3075                 }
3076                 section_sizes.removeFirst();
3077             } else {
3078                 newSectionLength = section_sizes.front();
3079                 section_sizes.removeFirst();
3080             }
3081         }
3082 
3083         //Q_ASSERT(newSectionLength > 0);
3084         if ((previousSectionResizeMode != newSectionResizeMode
3085             || previousSectionLength != newSectionLength) && i > 0) {
3086             int spanLength = (i - spanStartSection) * previousSectionLength;
3087             createSectionSpan(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
3088             //Q_ASSERT(headerLength() == length);
3089             spanStartSection = i;
3090         }
3091 
3092         if (newSectionLength != oldSectionLength)
3093             emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
3094 
3095         previousSectionLength = newSectionLength;
3096         previousSectionResizeMode = newSectionResizeMode;
3097     }
3098 
3099     createSectionSpan(spanStartSection, sectionCount - 1,
3100                       (sectionCount - spanStartSection) * previousSectionLength,
3101                       previousSectionResizeMode);
3102     //Q_ASSERT(headerLength() == length);
3103     resizeRecursionBlock = false;
3104     viewport->update();
3105 }
3106 
createSectionSpan(int start,int end,int size,QHeaderView::ResizeMode mode)3107 void QHeaderViewPrivate::createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode)
3108 {
3109     // ### the code for merging spans does not merge at all opertuneties
3110     // ### what if the number of sections is reduced ?
3111 
3112     SectionSpan span(size, (end - start) + 1, mode);
3113     int start_section = 0;
3114 #ifndef QT_NO_DEBUG
3115     int initial_section_count = headerSectionCount(); // ### debug code
3116 #endif
3117 
3118     QList<int> spansToRemove;
3119     for (int i = 0; i < sectionSpans.count(); ++i) {
3120         int end_section = start_section + sectionSpans.at(i).count - 1;
3121         int section_count = sectionSpans.at(i).count;
3122         if (start <= start_section && end > end_section) {
3123             // the existing span is entirely coveded by the new span
3124             spansToRemove.append(i);
3125         } else if (start < start_section && end >= end_section) {
3126             // the existing span is entirely coveded by the new span
3127             spansToRemove.append(i);
3128         } else if (start == start_section && end == end_section) {
3129             // the new span is covered by an existin span
3130             length -= sectionSpans.at(i).size;
3131             length += size;
3132             sectionSpans[i].size = size;
3133             sectionSpans[i].resizeMode = mode;
3134             // ### check if we can merge the section with any of its neighbours
3135             removeSpans(spansToRemove);
3136             Q_ASSERT(initial_section_count == headerSectionCount());
3137             return;
3138         } else if (start > start_section && end < end_section) {
3139             if (sectionSpans.at(i).sectionSize() == span.sectionSize()
3140                 && sectionSpans.at(i).resizeMode == span.resizeMode) {
3141                 Q_ASSERT(initial_section_count == headerSectionCount());
3142                 return;
3143             }
3144             // the new span is in the middle of the old span, so we have to split it
3145             length -= sectionSpans.at(i).size;
3146             int section_size = sectionSpans.at(i).sectionSize();
3147 #ifndef QT_NO_DEBUG
3148             int span_count = sectionSpans.at(i).count;
3149 #endif
3150             QHeaderView::ResizeMode span_mode = sectionSpans.at(i).resizeMode;
3151             // first span
3152             int first_span_count = start - start_section;
3153             int first_span_size = section_size * first_span_count;
3154             sectionSpans[i].count = first_span_count;
3155             sectionSpans[i].size = first_span_size;
3156             sectionSpans[i].resizeMode = span_mode;
3157             length += first_span_size;
3158             // middle span (the new span)
3159 #ifndef QT_NO_DEBUG
3160             int mid_span_count = span.count;
3161 #endif
3162             int mid_span_size = span.size;
3163             sectionSpans.insert(i + 1, span);
3164             length += mid_span_size;
3165             // last span
3166             int last_span_count = end_section - end;
3167             int last_span_size = section_size * last_span_count;
3168             sectionSpans.insert(i + 2, SectionSpan(last_span_size, last_span_count, span_mode));
3169             length += last_span_size;
3170             Q_ASSERT(span_count == first_span_count + mid_span_count + last_span_count);
3171             removeSpans(spansToRemove);
3172             Q_ASSERT(initial_section_count == headerSectionCount());
3173             return;
3174         } else if (start > start_section && start <= end_section && end >= end_section) {
3175             // the new span covers the last part of the existing span
3176             length -= sectionSpans.at(i).size;
3177             int removed_count = (end_section - start + 1);
3178             int span_count = sectionSpans.at(i).count - removed_count;
3179             int section_size = sectionSpans.at(i).sectionSize();
3180             int span_size = section_size * span_count;
3181             sectionSpans[i].count = span_count;
3182             sectionSpans[i].size = span_size;
3183             length += span_size;
3184             if (end == end_section) {
3185                 sectionSpans.insert(i + 1, span); // insert after
3186                 length += span.size;
3187                 removeSpans(spansToRemove);
3188                 Q_ASSERT(initial_section_count == headerSectionCount());
3189                 return;
3190             }
3191         } else if (end < end_section && end >= start_section && start <= start_section) {
3192             // the new span covers the first part of the existing span
3193             length -= sectionSpans.at(i).size;
3194             int removed_count = (end - start_section + 1);
3195             int section_size = sectionSpans.at(i).sectionSize();
3196             int span_count = sectionSpans.at(i).count - removed_count;
3197             int span_size = section_size * span_count;
3198             sectionSpans[i].count = span_count;
3199             sectionSpans[i].size = span_size;
3200             length += span_size;
3201             sectionSpans.insert(i, span); // insert before
3202             length += span.size;
3203             removeSpans(spansToRemove);
3204             Q_ASSERT(initial_section_count == headerSectionCount());
3205             return;
3206         }
3207         start_section += section_count;
3208     }
3209 
3210     // ### adding and removing _ sections_  in addition to spans
3211     // ### add some more checks here
3212 
3213     if (spansToRemove.isEmpty()) {
3214         if (!sectionSpans.isEmpty()
3215             && sectionSpans.last().sectionSize() == span.sectionSize()
3216             && sectionSpans.last().resizeMode == span.resizeMode) {
3217             length += span.size;
3218             int last = sectionSpans.count() - 1;
3219             sectionSpans[last].count += span.count;
3220             sectionSpans[last].size += span.size;
3221             sectionSpans[last].resizeMode = span.resizeMode;
3222         } else {
3223             length += span.size;
3224             sectionSpans.append(span);
3225         }
3226     } else {
3227         removeSpans(spansToRemove);
3228         length += span.size;
3229         sectionSpans.insert(spansToRemove.first(), span);
3230         //Q_ASSERT(initial_section_count == headerSectionCount());
3231     }
3232 }
3233 
removeSectionsFromSpans(int start,int end)3234 void QHeaderViewPrivate::removeSectionsFromSpans(int start, int end)
3235 {
3236     // remove sections
3237     int start_section = 0;
3238     QList<int> spansToRemove;
3239     for (int i = 0; i < sectionSpans.count(); ++i) {
3240         int end_section = start_section + sectionSpans.at(i).count - 1;
3241         int section_size = sectionSpans.at(i).sectionSize();
3242         int section_count = sectionSpans.at(i).count;
3243         if (start <= start_section && end >= end_section) {
3244             // the change covers the entire span
3245             spansToRemove.append(i);
3246             if (end == end_section)
3247                 break;
3248         } else if (start > start_section && end < end_section) {
3249             // all the removed sections are inside the span
3250             int change = (end - start + 1);
3251             sectionSpans[i].count -= change;
3252             sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3253             length -= (change * section_size);
3254             break;
3255         } else if (start >= start_section && start <= end_section) {
3256             // the some of the removed sections are inside the span,at the end
3257             int change = qMin(end_section - start + 1, end - start + 1);
3258             sectionSpans[i].count -= change;
3259             sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3260             start += change;
3261             length -= (change * section_size);
3262             // the change affects several spans
3263         } else if (end >= start_section && end <= end_section) {
3264             // the some of the removed sections are inside the span, at the beginning
3265             int change = qMin((end - start_section + 1), end - start + 1);
3266             sectionSpans[i].count -= change;
3267             sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3268             length -= (change * section_size);
3269             break;
3270         }
3271         start_section += section_count;
3272     }
3273 
3274     for (int i = spansToRemove.count() - 1; i >= 0; --i) {
3275         int s = spansToRemove.at(i);
3276         length -= sectionSpans.at(s).size;
3277         sectionSpans.remove(s);
3278         // ### merge remaining spans
3279     }
3280 }
3281 
clear()3282 void QHeaderViewPrivate::clear()
3283 {
3284     if (state != NoClear) {
3285     length = 0;
3286     sectionCount = 0;
3287     visualIndices.clear();
3288     logicalIndices.clear();
3289     sectionSelected.clear();
3290     sectionHidden.clear();
3291     hiddenSectionSize.clear();
3292     sectionSpans.clear();
3293     invalidateCachedSizeHint();
3294     }
3295 }
3296 
flipSortIndicator(int section)3297 void QHeaderViewPrivate::flipSortIndicator(int section)
3298 {
3299     Q_Q(QHeaderView);
3300     Qt::SortOrder sortOrder;
3301     if (sortIndicatorSection == section) {
3302         sortOrder = (sortIndicatorOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder;
3303     } else {
3304         const QVariant value = model->headerData(section, orientation, Qt::InitialSortOrderRole);
3305         if (value.canConvert(QVariant::Int))
3306             sortOrder = static_cast<Qt::SortOrder>(value.toInt());
3307         else
3308             sortOrder = Qt::AscendingOrder;
3309     }
3310     q->setSortIndicator(section, sortOrder);
3311 }
3312 
cascadingResize(int visual,int newSize)3313 void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
3314 {
3315     Q_Q(QHeaderView);
3316     const int minimumSize = q->minimumSectionSize();
3317     const int oldSize = headerSectionSize(visual);
3318     int delta = newSize - oldSize;
3319 
3320     if (delta > 0) { // larger
3321         bool sectionResized = false;
3322 
3323         // restore old section sizes
3324         for (int i = firstCascadingSection; i < visual; ++i) {
3325             if (cascadingSectionSize.contains(i)) {
3326                 int currentSectionSize = headerSectionSize(i);
3327                 int originalSectionSize = cascadingSectionSize.value(i);
3328                 if (currentSectionSize < originalSectionSize) {
3329                     int newSectionSize = currentSectionSize + delta;
3330                     resizeSectionSpan(i, currentSectionSize, newSectionSize);
3331                     if (newSectionSize >= originalSectionSize && false)
3332                         cascadingSectionSize.remove(i); // the section is now restored
3333                     sectionResized = true;
3334                     break;
3335                 }
3336             }
3337 
3338         }
3339 
3340         // resize the section
3341         if (!sectionResized) {
3342             newSize = qMax(newSize, minimumSize);
3343             if (oldSize != newSize)
3344                 resizeSectionSpan(visual, oldSize, newSize);
3345         }
3346 
3347         // cascade the section size change
3348         for (int i = visual + 1; i < sectionCount; ++i) {
3349             if (!sectionIsCascadable(i))
3350                 continue;
3351             int currentSectionSize = headerSectionSize(i);
3352             if (currentSectionSize <= minimumSize)
3353                 continue;
3354             int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3355             //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
3356             resizeSectionSpan(i, currentSectionSize, newSectionSize);
3357             saveCascadingSectionSize(i, currentSectionSize);
3358             delta = delta - (currentSectionSize - newSectionSize);
3359             //qDebug() << "new delta" << delta;
3360             //if (newSectionSize != minimumSize)
3361             if (delta <= 0)
3362                 break;
3363         }
3364     } else { // smaller
3365         bool sectionResized = false;
3366 
3367         // restore old section sizes
3368         for (int i = lastCascadingSection; i > visual; --i) {
3369             if (!cascadingSectionSize.contains(i))
3370                 continue;
3371             int currentSectionSize = headerSectionSize(i);
3372             int originalSectionSize = cascadingSectionSize.value(i);
3373             if (currentSectionSize >= originalSectionSize)
3374                 continue;
3375             int newSectionSize = currentSectionSize - delta;
3376             resizeSectionSpan(i, currentSectionSize, newSectionSize);
3377             if (newSectionSize >= originalSectionSize && false) {
3378                 //qDebug() << "section" << i << "restored to" << originalSectionSize;
3379                 cascadingSectionSize.remove(i); // the section is now restored
3380             }
3381             sectionResized = true;
3382             break;
3383         }
3384 
3385         // resize the section
3386         resizeSectionSpan(visual, oldSize, qMax(newSize, minimumSize));
3387 
3388         // cascade the section size change
3389         if (delta < 0 && newSize < minimumSize) {
3390             for (int i = visual - 1; i >= 0; --i) {
3391                 if (!sectionIsCascadable(i))
3392                     continue;
3393                 int sectionSize = headerSectionSize(i);
3394                 if (sectionSize <= minimumSize)
3395                     continue;
3396                 resizeSectionSpan(i, sectionSize, qMax(sectionSize + delta, minimumSize));
3397                 saveCascadingSectionSize(i, sectionSize);
3398                 break;
3399             }
3400         }
3401 
3402         // let the next section get the space from the resized section
3403         if (!sectionResized) {
3404             for (int i = visual + 1; i < sectionCount; ++i) {
3405                 if (!sectionIsCascadable(i))
3406                     continue;
3407                 int currentSectionSize = headerSectionSize(i);
3408                 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3409                 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3410                 break;
3411             }
3412         }
3413     }
3414 
3415     if (hasAutoResizeSections())
3416         doDelayedResizeSections();
3417 
3418     viewport->update();
3419 }
3420 
setDefaultSectionSize(int size)3421 void QHeaderViewPrivate::setDefaultSectionSize(int size)
3422 {
3423     Q_Q(QHeaderView);
3424     defaultSectionSize = size;
3425     int currentVisualIndex = 0;
3426     for (int i = 0; i < sectionSpans.count(); ++i) {
3427         QHeaderViewPrivate::SectionSpan &span = sectionSpans[i];
3428         if (span.size > 0) {
3429             //we resize it if it is not hidden (ie size > 0)
3430             const int newSize = span.count * size;
3431             if (newSize != span.size) {
3432                 length += newSize - span.size; //the whole length is changed
3433                 const int oldSectionSize = span.sectionSize();
3434                 span.size = span.count * size;
3435                 for (int i = currentVisualIndex; i < currentVisualIndex + span.count; ++i) {
3436                     emit q->sectionResized(logicalIndex(i), oldSectionSize, size);
3437                 }
3438             }
3439         }
3440         currentVisualIndex += span.count;
3441     }
3442 }
3443 
resizeSectionSpan(int visualIndex,int oldSize,int newSize)3444 void QHeaderViewPrivate::resizeSectionSpan(int visualIndex, int oldSize, int newSize)
3445 {
3446     Q_Q(QHeaderView);
3447     QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
3448     createSectionSpan(visualIndex, visualIndex, newSize, mode);
3449     emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
3450 }
3451 
headerSectionSize(int visual) const3452 int QHeaderViewPrivate::headerSectionSize(int visual) const
3453 {
3454     // ### stupid iteration
3455     int section_start = 0;
3456     const int sectionSpansCount = sectionSpans.count();
3457     for (int i = 0; i < sectionSpansCount; ++i) {
3458         const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3459         int section_end = section_start + currentSection.count - 1;
3460         if (visual >= section_start && visual <= section_end)
3461             return currentSection.sectionSize();
3462         section_start = section_end + 1;
3463     }
3464     return -1;
3465 }
3466 
headerSectionPosition(int visual) const3467 int QHeaderViewPrivate::headerSectionPosition(int visual) const
3468 {
3469     // ### stupid iteration
3470     int section_start = 0;
3471     int span_position = 0;
3472     const int sectionSpansCount = sectionSpans.count();
3473     for (int i = 0; i < sectionSpansCount; ++i) {
3474         const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3475         int section_end = section_start + currentSection.count - 1;
3476         if (visual >= section_start && visual <= section_end)
3477             return span_position + (visual - section_start) * currentSection.sectionSize();
3478         section_start = section_end + 1;
3479         span_position += currentSection.size;
3480     }
3481     return -1;
3482 }
3483 
headerVisualIndexAt(int position) const3484 int QHeaderViewPrivate::headerVisualIndexAt(int position) const
3485 {
3486     // ### stupid iteration
3487     int span_start_section = 0;
3488     int span_position = 0;
3489     const int sectionSpansCount = sectionSpans.count();
3490     for (int i = 0; i < sectionSpansCount; ++i) {
3491         const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3492         int next_span_start_section = span_start_section + currentSection.count;
3493         int next_span_position = span_position + currentSection.size;
3494         if (position == span_position && currentSection.size > 0)
3495             return span_start_section;
3496         if (position > span_position && position < next_span_position) {
3497             int position_in_span = position - span_position;
3498             return span_start_section + (position_in_span / currentSection.sectionSize());
3499         }
3500         span_start_section = next_span_start_section;
3501         span_position = next_span_position;
3502     }
3503     return -1;
3504 }
3505 
setHeaderSectionResizeMode(int visual,QHeaderView::ResizeMode mode)3506 void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
3507 {
3508     int size = headerSectionSize(visual);
3509     createSectionSpan(visual, visual, size, mode);
3510 }
3511 
headerSectionResizeMode(int visual) const3512 QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
3513 {
3514     int span = sectionSpanIndex(visual);
3515     if (span == -1)
3516         return globalResizeMode;
3517     return sectionSpans.at(span).resizeMode;
3518 }
3519 
setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)3520 void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
3521 {
3522     globalResizeMode = mode;
3523     for (int i = 0; i < sectionSpans.count(); ++i)
3524         sectionSpans[i].resizeMode = mode;
3525 }
3526 
viewSectionSizeHint(int logical) const3527 int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
3528 {
3529     if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) {
3530         return (orientation == Qt::Horizontal
3531                 ? view->sizeHintForColumn(logical)
3532                 : view->sizeHintForRow(logical));
3533     }
3534     return 0;
3535 }
3536 
adjustedVisualIndex(int visualIndex) const3537 int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
3538 {
3539     if (hiddenSectionSize.count() > 0) {
3540         int adjustedVisualIndex = visualIndex;
3541         int currentVisualIndex = 0;
3542         for (int i = 0; i < sectionSpans.count(); ++i) {
3543             if (sectionSpans.at(i).size == 0)
3544                 adjustedVisualIndex += sectionSpans.at(i).count;
3545             else
3546                 currentVisualIndex += sectionSpans.at(i).count;
3547             if (currentVisualIndex >= visualIndex)
3548                 break;
3549         }
3550         visualIndex = adjustedVisualIndex;
3551     }
3552     return visualIndex;
3553 }
3554 
3555 #ifndef QT_NO_DATASTREAM
write(QDataStream & out) const3556 void QHeaderViewPrivate::write(QDataStream &out) const
3557 {
3558     out << int(orientation);
3559     out << int(sortIndicatorOrder);
3560     out << sortIndicatorSection;
3561     out << sortIndicatorShown;
3562 
3563     out << visualIndices;
3564     out << logicalIndices;
3565 
3566     out << sectionHidden;
3567     out << hiddenSectionSize;
3568 
3569     out << length;
3570     out << sectionCount;
3571     out << movableSections;
3572     out << clickableSections;
3573     out << highlightSelected;
3574     out << stretchLastSection;
3575     out << cascadingResizing;
3576     out << stretchSections;
3577     out << contentsSections;
3578     out << defaultSectionSize;
3579     out << minimumSectionSize;
3580 
3581     out << int(defaultAlignment);
3582     out << int(globalResizeMode);
3583 
3584     out << sectionSpans;
3585 }
3586 
read(QDataStream & in)3587 bool QHeaderViewPrivate::read(QDataStream &in)
3588 {
3589     int orient, order, align, global;
3590     in >> orient;
3591     orientation = (Qt::Orientation)orient;
3592 
3593     in >> order;
3594     sortIndicatorOrder = (Qt::SortOrder)order;
3595 
3596     in >> sortIndicatorSection;
3597     in >> sortIndicatorShown;
3598 
3599     in >> visualIndices;
3600     in >> logicalIndices;
3601 
3602     in >> sectionHidden;
3603     in >> hiddenSectionSize;
3604 
3605     in >> length;
3606     in >> sectionCount;
3607     in >> movableSections;
3608     in >> clickableSections;
3609     in >> highlightSelected;
3610     in >> stretchLastSection;
3611     in >> cascadingResizing;
3612     in >> stretchSections;
3613     in >> contentsSections;
3614     in >> defaultSectionSize;
3615     in >> minimumSectionSize;
3616 
3617     in >> align;
3618     defaultAlignment = Qt::Alignment(align);
3619 
3620     in >> global;
3621     globalResizeMode = (QHeaderView::ResizeMode)global;
3622 
3623     in >> sectionSpans;
3624 
3625     return true;
3626 }
3627 
3628 #endif // QT_NO_DATASTREAM
3629 
3630 QT_END_NAMESPACE
3631 
3632 #endif // QT_NO_ITEMVIEWS
3633 
3634 #include "moc_qheaderview.cpp"
3635