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 ¤t, 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 ¤tSection = sectionSpans.at(i);
3459 int section_end = section_start + currentSection.count - 1;
3460 if (visual >= section_start && visual <= section_end)
3461 return currentSection.sectionSize();
3462 section_start = section_end + 1;
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 ¤tSection = sectionSpans.at(i);
3475 int section_end = section_start + currentSection.count - 1;
3476 if (visual >= section_start && visual <= section_end)
3477 return span_position + (visual - section_start) * currentSection.sectionSize();
3478 section_start = section_end + 1;
3479 span_position += currentSection.size;
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 ¤tSection = sectionSpans.at(i);
3492 int next_span_start_section = span_start_section + currentSection.count;
3493 int next_span_position = span_position + currentSection.size;
3494 if (position == span_position && currentSection.size > 0)
3495 return span_start_section;
3496 if (position > span_position && position < next_span_position) {
3497 int position_in_span = position - span_position;
3498 return span_start_section + (position_in_span / currentSection.sectionSize());
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