source: trunk/src/gui/itemviews/qheaderview.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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