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

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 114.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 j = logicalLast + 1; j < sectionHidden.count(); ++j)
1703 //here we simply copy the old sectionHidden
1704 sectionHidden.setBit(j, d->sectionHidden.testBit(j - insertCount));
1705 d->sectionHidden = sectionHidden;
1706 }
1707
1708 // insert sections into hiddenSectionSize
1709 QHash<int, int> newHiddenSectionSize; // from logical index to section size
1710 for (int i = 0; i < logicalFirst; ++i)
1711 if (isSectionHidden(i))
1712 newHiddenSectionSize[i] = d->hiddenSectionSize[i];
1713 for (int j = logicalLast + 1; j < d->sectionCount; ++j)
1714 if (isSectionHidden(j))
1715 newHiddenSectionSize[j] = d->hiddenSectionSize[j - insertCount];
1716 d->hiddenSectionSize = newHiddenSectionSize;
1717
1718 d->doDelayedResizeSections();
1719 emit sectionCountChanged(oldCount, count());
1720
1721 // if the new sections were not updated by resizing, we need to update now
1722 if (!d->hasAutoResizeSections())
1723 d->viewport->update();
1724}
1725
1726/*!
1727 This slot is called when sections are removed from the \a parent.
1728 \a logicalFirst and \a logicalLast signify where the sections were removed.
1729
1730 If only one section is removed, \a logicalFirst and \a logicalLast will
1731 be the same.
1732*/
1733
1734void QHeaderView::sectionsAboutToBeRemoved(const QModelIndex &parent,
1735 int logicalFirst, int logicalLast)
1736{
1737 Q_UNUSED(parent);
1738 Q_UNUSED(logicalFirst);
1739 Q_UNUSED(logicalLast);
1740}
1741
1742void QHeaderViewPrivate::updateHiddenSections(int logicalFirst, int logicalLast)
1743{
1744 Q_Q(QHeaderView);
1745 const int changeCount = logicalLast - logicalFirst + 1;
1746
1747 // remove sections from hiddenSectionSize
1748 QHash<int, int> newHiddenSectionSize; // from logical index to section size
1749 for (int i = 0; i < logicalFirst; ++i)
1750 if (q->isSectionHidden(i))
1751 newHiddenSectionSize[i] = hiddenSectionSize[i];
1752 for (int j = logicalLast + 1; j < sectionCount; ++j)
1753 if (q->isSectionHidden(j))
1754 newHiddenSectionSize[j - changeCount] = hiddenSectionSize[j];
1755 hiddenSectionSize = newHiddenSectionSize;
1756
1757 // remove sections from sectionsHidden
1758 if (!sectionHidden.isEmpty()) {
1759 const int newsize = qMin(sectionCount - changeCount, sectionHidden.size());
1760 QBitArray newSectionHidden(newsize);
1761 for (int j = 0, k = 0; j < sectionHidden.size(); ++j) {
1762 const int logical = logicalIndex(j);
1763 if (logical < logicalFirst || logical > logicalLast) {
1764 newSectionHidden[k++] = sectionHidden[j];
1765 }
1766 }
1767 sectionHidden = newSectionHidden;
1768 }
1769}
1770
1771void QHeaderViewPrivate::_q_sectionsRemoved(const QModelIndex &parent,
1772 int logicalFirst, int logicalLast)
1773{
1774 Q_Q(QHeaderView);
1775 if (parent != root)
1776 return; // we only handle changes in the top level
1777 if (qMin(logicalFirst, logicalLast) < 0
1778 || qMax(logicalLast, logicalFirst) >= sectionCount)
1779 return;
1780 int oldCount = q->count();
1781 int changeCount = logicalLast - logicalFirst + 1;
1782
1783 updateHiddenSections(logicalFirst, logicalLast);
1784
1785 if (visualIndices.isEmpty() && logicalIndices.isEmpty()) {
1786 //Q_ASSERT(headerSectionCount() == sectionCount);
1787 removeSectionsFromSpans(logicalFirst, logicalLast);
1788 } else {
1789 for (int l = logicalLast; l >= logicalFirst; --l) {
1790 int visual = visualIndices.at(l);
1791 for (int v = 0; v < sectionCount; ++v) {
1792 if (v >= logicalIndices.count())
1793 continue; // the section doesn't exist
1794 if (v > visual) {
1795 int logical = logicalIndices.at(v);
1796 --(visualIndices[logical]);
1797 }
1798 if (logicalIndex(v) > l) // no need to move the positions before l
1799 --(logicalIndices[v]);
1800 }
1801 logicalIndices.remove(visual);
1802 visualIndices.remove(l);
1803 //Q_ASSERT(headerSectionCount() == sectionCount);
1804 removeSectionsFromSpans(visual, visual);
1805 }
1806 // ### handle sectionSelection, sectionHidden
1807 }
1808 sectionCount -= changeCount;
1809
1810 // update sorting column
1811 if (sortIndicatorSection >= logicalFirst) {
1812 if (sortIndicatorSection <= logicalLast)
1813 sortIndicatorSection = -1;
1814 else
1815 sortIndicatorSection -= changeCount;
1816 }
1817
1818 // if we only have the last section (the "end" position) left, the header is empty
1819 if (sectionCount <= 0)
1820 clear();
1821 invalidateCachedSizeHint();
1822 emit q->sectionCountChanged(oldCount, q->count());
1823 viewport->update();
1824}
1825
1826void QHeaderViewPrivate::_q_layoutAboutToBeChanged()
1827{
1828 //if there is no row/column we can't have mapping for columns
1829 //because no QModelIndex in the model would be valid
1830 // ### this is far from being bullet-proof and we would need a real system to
1831 // ### map columns or rows persistently
1832 if ((orientation == Qt::Horizontal && model->rowCount(root) == 0)
1833 || model->columnCount(root) == 0)
1834 return;
1835
1836 for (int i = 0; i < sectionHidden.count(); ++i)
1837 if (sectionHidden.testBit(i)) // ### note that we are using column or row 0
1838 persistentHiddenSections.append(orientation == Qt::Horizontal
1839 ? model->index(0, logicalIndex(i), root)
1840 : model->index(logicalIndex(i), 0, root));
1841}
1842
1843void QHeaderViewPrivate::_q_layoutChanged()
1844{
1845 Q_Q(QHeaderView);
1846 viewport->update();
1847 if (persistentHiddenSections.isEmpty() || modelIsEmpty()) {
1848 if (modelSectionCount() != sectionCount)
1849 q->initializeSections();
1850 persistentHiddenSections.clear();
1851 return;
1852 }
1853
1854 QBitArray oldSectionHidden = sectionHidden;
1855 bool sectionCountChanged = false;
1856
1857 for (int i = 0; i < persistentHiddenSections.count(); ++i) {
1858 QModelIndex index = persistentHiddenSections.at(i);
1859 if (index.isValid()) {
1860 const int logical = (orientation == Qt::Horizontal
1861 ? index.column()
1862 : index.row());
1863 q->setSectionHidden(logical, true);
1864 oldSectionHidden.setBit(logical, false);
1865 } else if (!sectionCountChanged && (modelSectionCount() != sectionCount)) {
1866 sectionCountChanged = true;
1867 break;
1868 }
1869 }
1870 persistentHiddenSections.clear();
1871
1872 for (int i = 0; i < oldSectionHidden.count(); ++i) {
1873 if (oldSectionHidden.testBit(i))
1874 q->setSectionHidden(i, false);
1875 }
1876
1877 // the number of sections changed; we need to reread the state of the model
1878 if (sectionCountChanged)
1879 q->initializeSections();
1880}
1881
1882/*!
1883 \internal
1884*/
1885
1886void QHeaderView::initializeSections()
1887{
1888 Q_D(QHeaderView);
1889 const int oldCount = d->sectionCount;
1890 const int newCount = d->modelSectionCount();
1891 if (newCount <= 0) {
1892 d->clear();
1893 emit sectionCountChanged(oldCount, 0);
1894 } else if (newCount != oldCount) {
1895 const int min = qBound(0, oldCount, newCount - 1);
1896 initializeSections(min, newCount - 1);
1897 if (stretchLastSection()) // we've already gotten the size hint
1898 d->lastSectionSize = sectionSize(logicalIndex(d->sectionCount - 1));
1899
1900 //make sure we update the hidden sections
1901 if (newCount < oldCount)
1902 d->updateHiddenSections(0, newCount-1);
1903 }
1904}
1905
1906/*!
1907 \internal
1908*/
1909
1910void QHeaderView::initializeSections(int start, int end)
1911{
1912 Q_D(QHeaderView);
1913
1914 Q_ASSERT(start >= 0);
1915 Q_ASSERT(end >= 0);
1916
1917 d->invalidateCachedSizeHint();
1918
1919 if (end + 1 < d->sectionCount) {
1920 int newCount = end + 1;
1921 d->removeSectionsFromSpans(newCount, d->sectionCount);
1922 if (!d->hiddenSectionSize.isEmpty()) {
1923 if (d->sectionCount - newCount > d->hiddenSectionSize.count()) {
1924 for (int i = end + 1; i < d->sectionCount; ++i)
1925 d->hiddenSectionSize.remove(i);
1926 } else {
1927 QHash<int, int>::iterator it = d->hiddenSectionSize.begin();
1928 while (it != d->hiddenSectionSize.end()) {
1929 if (it.key() > end)
1930 it = d->hiddenSectionSize.erase(it);
1931 else
1932 ++it;
1933 }
1934 }
1935 }
1936 }
1937
1938 int oldCount = d->sectionCount;
1939 d->sectionCount = end + 1;
1940
1941 if (!d->logicalIndices.isEmpty()) {
1942 if (oldCount <= d->sectionCount) {
1943 d->logicalIndices.resize(d->sectionCount);
1944 d->visualIndices.resize(d->sectionCount);
1945 for (int i = oldCount; i < d->sectionCount; ++i) {
1946 d->logicalIndices[i] = i;
1947 d->visualIndices[i] = i;
1948 }
1949 } else {
1950 int j = 0;
1951 for (int i = 0; i < oldCount; ++i) {
1952 int v = d->logicalIndices.at(i);
1953 if (v < d->sectionCount) {
1954 d->logicalIndices[j] = v;
1955 d->visualIndices[v] = j;
1956 j++;
1957 }
1958 }
1959 d->logicalIndices.resize(d->sectionCount);
1960 d->visualIndices.resize(d->sectionCount);
1961 }
1962 }
1963
1964 if (d->globalResizeMode == Stretch)
1965 d->stretchSections = d->sectionCount;
1966 else if (d->globalResizeMode == ResizeToContents)
1967 d->contentsSections = d->sectionCount;
1968 if (!d->sectionHidden.isEmpty())
1969 d->sectionHidden.resize(d->sectionCount);
1970
1971 if (d->sectionCount > oldCount)
1972 d->createSectionSpan(start, end, (end - start + 1) * d->defaultSectionSize, d->globalResizeMode);
1973 //Q_ASSERT(d->headerLength() == d->length);
1974
1975 if (d->sectionCount != oldCount)
1976 emit sectionCountChanged(oldCount, d->sectionCount);
1977 d->viewport->update();
1978}
1979
1980/*!
1981 \reimp
1982*/
1983
1984void QHeaderView::currentChanged(const QModelIndex &current, const QModelIndex &old)
1985{
1986 Q_D(QHeaderView);
1987
1988 if (d->orientation == Qt::Horizontal && current.column() != old.column()) {
1989 if (old.isValid() && old.parent() == d->root)
1990 d->viewport->update(QRect(sectionViewportPosition(old.column()), 0,
1991 sectionSize(old.column()), d->viewport->height()));
1992 if (current.isValid() && current.parent() == d->root)
1993 d->viewport->update(QRect(sectionViewportPosition(current.column()), 0,
1994 sectionSize(current.column()), d->viewport->height()));
1995 } else if (d->orientation == Qt::Vertical && current.row() != old.row()) {
1996 if (old.isValid() && old.parent() == d->root)
1997 d->viewport->update(QRect(0, sectionViewportPosition(old.row()),
1998 d->viewport->width(), sectionSize(old.row())));
1999 if (current.isValid() && current.parent() == d->root)
2000 d->viewport->update(QRect(0, sectionViewportPosition(current.row()),
2001 d->viewport->width(), sectionSize(current.row())));
2002 }
2003}
2004
2005
2006/*!
2007 \reimp
2008*/
2009
2010bool QHeaderView::event(QEvent *e)
2011{
2012 Q_D(QHeaderView);
2013 switch (e->type()) {
2014 case QEvent::HoverEnter: {
2015 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2016 d->hover = logicalIndexAt(he->pos());
2017 if (d->hover != -1)
2018 updateSection(d->hover);
2019 break; }
2020 case QEvent::Leave:
2021 case QEvent::HoverLeave: {
2022 if (d->hover != -1)
2023 updateSection(d->hover);
2024 d->hover = -1;
2025 break; }
2026 case QEvent::HoverMove: {
2027 QHoverEvent *he = static_cast<QHoverEvent*>(e);
2028 int oldHover = d->hover;
2029 d->hover = logicalIndexAt(he->pos());
2030 if (d->hover != oldHover) {
2031 if (oldHover != -1)
2032 updateSection(oldHover);
2033 if (d->hover != -1)
2034 updateSection(d->hover);
2035 }
2036 break; }
2037 case QEvent::Timer: {
2038 QTimerEvent *te = static_cast<QTimerEvent*>(e);
2039 if (te->timerId() == d->delayedResize.timerId()) {
2040 d->delayedResize.stop();
2041 resizeSections();
2042 }
2043 break; }
2044 default:
2045 break;
2046 }
2047 return QAbstractItemView::event(e);
2048}
2049
2050/*!
2051 \reimp
2052*/
2053
2054void QHeaderView::paintEvent(QPaintEvent *e)
2055{
2056 Q_D(QHeaderView);
2057
2058 if (count() == 0)
2059 return;
2060
2061 QPainter painter(d->viewport);
2062 const QPoint offset = d->scrollDelayOffset;
2063 QRect translatedEventRect = e->rect();
2064 translatedEventRect.translate(offset);
2065
2066 int start = -1;
2067 int end = -1;
2068 if (d->orientation == Qt::Horizontal) {
2069 start = visualIndexAt(translatedEventRect.left());
2070 end = visualIndexAt(translatedEventRect.right());
2071 } else {
2072 start = visualIndexAt(translatedEventRect.top());
2073 end = visualIndexAt(translatedEventRect.bottom());
2074 }
2075
2076 if (d->reverse()) {
2077 start = (start == -1 ? count() - 1 : start);
2078 end = (end == -1 ? 0 : end);
2079 } else {
2080 start = (start == -1 ? 0 : start);
2081 end = (end == -1 ? count() - 1 : end);
2082 }
2083
2084 int tmp = start;
2085 start = qMin(start, end);
2086 end = qMax(tmp, end);
2087
2088 d->prepareSectionSelected(); // clear and resize the bit array
2089
2090 QRect currentSectionRect;
2091 int logical;
2092 const int width = d->viewport->width();
2093 const int height = d->viewport->height();
2094 for (int i = start; i <= end; ++i) {
2095 if (d->isVisualIndexHidden(i))
2096 continue;
2097 painter.save();
2098 logical = logicalIndex(i);
2099 if (d->orientation == Qt::Horizontal) {
2100 currentSectionRect.setRect(sectionViewportPosition(logical), 0, sectionSize(logical), height);
2101 } else {
2102 currentSectionRect.setRect(0, sectionViewportPosition(logical), width, sectionSize(logical));
2103 }
2104 currentSectionRect.translate(offset);
2105
2106 QVariant variant = d->model->headerData(logical, d->orientation,
2107 Qt::FontRole);
2108 if (variant.isValid() && qVariantCanConvert<QFont>(variant)) {
2109 QFont sectionFont = qvariant_cast<QFont>(variant);
2110 painter.setFont(sectionFont);
2111 }
2112 paintSection(&painter, currentSectionRect, logical);
2113 painter.restore();
2114 }
2115
2116 QStyleOption opt;
2117 opt.init(this);
2118 // Paint the area beyond where there are indexes
2119 if (d->reverse()) {
2120 opt.state |= QStyle::State_Horizontal;
2121 if (currentSectionRect.left() > translatedEventRect.left()) {
2122 opt.rect = QRect(translatedEventRect.left(), 0,
2123 currentSectionRect.left() - translatedEventRect.left(), height);
2124 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2125 }
2126 } else if (currentSectionRect.right() < translatedEventRect.right()) {
2127 // paint to the right
2128 opt.state |= QStyle::State_Horizontal;
2129 opt.rect = QRect(currentSectionRect.right() + 1, 0,
2130 translatedEventRect.right() - currentSectionRect.right(), height);
2131 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2132 } else if (currentSectionRect.bottom() < translatedEventRect.bottom()) {
2133 // paint the bottom section
2134 opt.state &= ~QStyle::State_Horizontal;
2135 opt.rect = QRect(0, currentSectionRect.bottom() + 1,
2136 width, height - currentSectionRect.bottom() - 1);
2137 style()->drawControl(QStyle::CE_HeaderEmptyArea, &opt, &painter, this);
2138 }
2139
2140#if 0
2141 // ### visualize section spans
2142 for (int a = 0, i = 0; i < d->sectionSpans.count(); ++i) {
2143 QColor color((i & 4 ? 255 : 0), (i & 2 ? 255 : 0), (i & 1 ? 255 : 0));
2144 if (d->orientation == Qt::Horizontal)
2145 painter.fillRect(a - d->offset, 0, d->sectionSpans.at(i).size, 4, color);
2146 else
2147 painter.fillRect(0, a - d->offset, 4, d->sectionSpans.at(i).size, color);
2148 a += d->sectionSpans.at(i).size;
2149 }
2150
2151#endif
2152}
2153
2154/*!
2155 \reimp
2156*/
2157
2158void QHeaderView::mousePressEvent(QMouseEvent *e)
2159{
2160 Q_D(QHeaderView);
2161 if (d->state != QHeaderViewPrivate::NoState || e->button() != Qt::LeftButton)
2162 return;
2163 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2164 int handle = d->sectionHandleAt(pos);
2165 d->originalSize = -1; // clear the stored original size
2166 if (handle == -1) {
2167 d->pressed = logicalIndexAt(pos);
2168 if (d->clickableSections)
2169 emit sectionPressed(d->pressed);
2170 if (d->movableSections) {
2171 d->section = d->target = d->pressed;
2172 if (d->section == -1)
2173 return;
2174 d->state = QHeaderViewPrivate::MoveSection;
2175 d->setupSectionIndicator(d->section, pos);
2176 } else if (d->clickableSections && d->pressed != -1) {
2177 updateSection(d->pressed);
2178 d->state = QHeaderViewPrivate::SelectSections;
2179 }
2180 } else if (resizeMode(handle) == Interactive) {
2181 d->originalSize = sectionSize(handle);
2182 d->state = QHeaderViewPrivate::ResizeSection;
2183 d->section = handle;
2184 }
2185
2186 d->firstPos = pos;
2187 d->lastPos = pos;
2188
2189 d->clearCascadingSections();
2190}
2191
2192/*!
2193 \reimp
2194*/
2195
2196void QHeaderView::mouseMoveEvent(QMouseEvent *e)
2197{
2198 Q_D(QHeaderView);
2199 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2200 if (pos < 0)
2201 return;
2202 if (e->buttons() == Qt::NoButton) {
2203 d->state = QHeaderViewPrivate::NoState;
2204 d->pressed = -1;
2205 }
2206 switch (d->state) {
2207 case QHeaderViewPrivate::ResizeSection: {
2208 Q_ASSERT(d->originalSize != -1);
2209 if (d->cascadingResizing) {
2210 int delta = d->reverse() ? d->lastPos - pos : pos - d->lastPos;
2211 int visual = visualIndex(d->section);
2212 d->cascadingResize(visual, d->headerSectionSize(visual) + delta);
2213 } else {
2214 int delta = d->reverse() ? d->firstPos - pos : pos - d->firstPos;
2215 resizeSection(d->section, qMax(d->originalSize + delta, minimumSectionSize()));
2216 }
2217 d->lastPos = pos;
2218 return;
2219 }
2220 case QHeaderViewPrivate::MoveSection: {
2221 if (qAbs(pos - d->firstPos) >= QApplication::startDragDistance()
2222 || !d->sectionIndicator->isHidden()) {
2223 int visual = visualIndexAt(pos);
2224 if (visual == -1)
2225 return;
2226 int posThreshold = d->headerSectionPosition(visual) + d->headerSectionSize(visual) / 2;
2227 int moving = visualIndex(d->section);
2228 if (visual < moving) {
2229 if (pos < posThreshold)
2230 d->target = d->logicalIndex(visual);
2231 else
2232 d->target = d->logicalIndex(visual + 1);
2233 } else if (visual > moving) {
2234 if (pos > posThreshold)
2235 d->target = d->logicalIndex(visual);
2236 else
2237 d->target = d->logicalIndex(visual - 1);
2238 } else {
2239 d->target = d->section;
2240 }
2241 d->updateSectionIndicator(d->section, pos);
2242 }
2243 return;
2244 }
2245 case QHeaderViewPrivate::SelectSections: {
2246 int logical = logicalIndexAt(pos);
2247 if (logical == d->pressed)
2248 return; // nothing to do
2249 else if (d->pressed != -1)
2250 updateSection(d->pressed);
2251 d->pressed = logical;
2252 if (d->clickableSections && logical != -1) {
2253 emit sectionEntered(d->pressed);
2254 updateSection(d->pressed);
2255 }
2256 return;
2257 }
2258 case QHeaderViewPrivate::NoState: {
2259#ifndef QT_NO_CURSOR
2260 int handle = d->sectionHandleAt(pos);
2261 bool hasCursor = testAttribute(Qt::WA_SetCursor);
2262 if (handle != -1 && (resizeMode(handle) == Interactive)) {
2263 if (!hasCursor)
2264 setCursor(d->orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
2265 } else if (hasCursor) {
2266 unsetCursor();
2267 }
2268#endif
2269 return;
2270 }
2271 default:
2272 break;
2273 }
2274}
2275
2276/*!
2277 \reimp
2278*/
2279
2280void QHeaderView::mouseReleaseEvent(QMouseEvent *e)
2281{
2282 Q_D(QHeaderView);
2283 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2284 switch (d->state) {
2285 case QHeaderViewPrivate::MoveSection:
2286 if (!d->sectionIndicator->isHidden()) { // moving
2287 int from = visualIndex(d->section);
2288 Q_ASSERT(from != -1);
2289 int to = visualIndex(d->target);
2290 Q_ASSERT(to != -1);
2291 moveSection(from, to);
2292 d->section = d->target = -1;
2293 d->updateSectionIndicator(d->section, pos);
2294 break;
2295 } // not moving
2296 case QHeaderViewPrivate::SelectSections:
2297 if (!d->clickableSections) {
2298 int section = logicalIndexAt(pos);
2299 updateSection(section);
2300 }
2301 // fall through
2302 case QHeaderViewPrivate::NoState:
2303 if (d->clickableSections) {
2304 int section = logicalIndexAt(pos);
2305 if (section != -1 && section == d->pressed) {
2306 d->flipSortIndicator(section);
2307 emit sectionClicked(section);
2308 }
2309 if (d->pressed != -1)
2310 updateSection(d->pressed);
2311 }
2312 break;
2313 case QHeaderViewPrivate::ResizeSection:
2314 d->originalSize = -1;
2315 d->clearCascadingSections();
2316 break;
2317 default:
2318 break;
2319 }
2320 d->state = QHeaderViewPrivate::NoState;
2321 d->pressed = -1;
2322}
2323
2324/*!
2325 \reimp
2326*/
2327
2328void QHeaderView::mouseDoubleClickEvent(QMouseEvent *e)
2329{
2330 Q_D(QHeaderView);
2331 int pos = d->orientation == Qt::Horizontal ? e->x() : e->y();
2332 int handle = d->sectionHandleAt(pos);
2333 if (handle > -1 && resizeMode(handle) == Interactive) {
2334 emit sectionHandleDoubleClicked(handle);
2335#ifndef QT_NO_CURSOR
2336 Qt::CursorShape splitCursor = (d->orientation == Qt::Horizontal)
2337 ? Qt::SplitHCursor : Qt::SplitVCursor;
2338 if (cursor().shape() == splitCursor) {
2339 // signal handlers may have changed the section size
2340 handle = d->sectionHandleAt(pos);
2341 if (!(handle > -1 && resizeMode(handle) == Interactive))
2342 setCursor(Qt::ArrowCursor);
2343 }
2344#endif
2345 } else {
2346 emit sectionDoubleClicked(logicalIndexAt(e->pos()));
2347 }
2348}
2349
2350/*!
2351 \reimp
2352*/
2353
2354bool QHeaderView::viewportEvent(QEvent *e)
2355{
2356 Q_D(QHeaderView);
2357 switch (e->type()) {
2358#ifndef QT_NO_TOOLTIP
2359 case QEvent::ToolTip: {
2360 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2361 int logical = logicalIndexAt(he->pos());
2362 if (logical != -1) {
2363 QVariant variant = d->model->headerData(logical, d->orientation, Qt::ToolTipRole);
2364 if (variant.isValid()) {
2365 QToolTip::showText(he->globalPos(), variant.toString(), this);
2366 return true;
2367 }
2368 }
2369 break; }
2370#endif
2371#ifndef QT_NO_WHATSTHIS
2372 case QEvent::QueryWhatsThis: {
2373 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2374 int logical = logicalIndexAt(he->pos());
2375 if (logical != -1
2376 && d->model->headerData(logical, d->orientation, Qt::WhatsThisRole).isValid())
2377 return true;
2378 break; }
2379 case QEvent::WhatsThis: {
2380 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2381 int logical = logicalIndexAt(he->pos());
2382 if (logical != -1) {
2383 QVariant whatsthis = d->model->headerData(logical, d->orientation,
2384 Qt::WhatsThisRole);
2385 if (whatsthis.isValid()) {
2386 QWhatsThis::showText(he->globalPos(), whatsthis.toString(), this);
2387 return true;
2388 }
2389 }
2390 break; }
2391#endif // QT_NO_WHATSTHIS
2392#ifndef QT_NO_STATUSTIP
2393 case QEvent::StatusTip: {
2394 QHelpEvent *he = static_cast<QHelpEvent*>(e);
2395 int logical = logicalIndexAt(he->pos());
2396 if (logical != -1) {
2397 QString statustip = d->model->headerData(logical, d->orientation,
2398 Qt::StatusTipRole).toString();
2399 if (!statustip.isEmpty())
2400 setStatusTip(statustip);
2401 }
2402 return true; }
2403#endif // QT_NO_STATUSTIP
2404 case QEvent::Hide:
2405 case QEvent::Show:
2406 case QEvent::FontChange:
2407 case QEvent::StyleChange:
2408 d->invalidateCachedSizeHint();
2409 resizeSections();
2410 emit geometriesChanged();
2411 break;
2412 case QEvent::ContextMenu: {
2413 d->state = QHeaderViewPrivate::NoState;
2414 d->pressed = d->section = d->target = -1;
2415 d->updateSectionIndicator(d->section, -1);
2416 break; }
2417 case QEvent::Wheel: {
2418 QAbstractScrollArea *asa = qobject_cast<QAbstractScrollArea *>(parentWidget());
2419 if (asa)
2420 return QApplication::sendEvent(asa->viewport(), e);
2421 break; }
2422 default:
2423 break;
2424 }
2425 return QAbstractItemView::viewportEvent(e);
2426}
2427
2428/*!
2429 Paints the section specified by the given \a logicalIndex, using the given
2430 \a painter and \a rect.
2431
2432 Normally, you do not have to call this function.
2433*/
2434
2435void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
2436{
2437 Q_D(const QHeaderView);
2438 if (!rect.isValid())
2439 return;
2440 // get the state of the section
2441 QStyleOptionHeader opt;
2442 initStyleOption(&opt);
2443 QStyle::State state = QStyle::State_None;
2444 if (isEnabled())
2445 state |= QStyle::State_Enabled;
2446 if (window()->isActiveWindow())
2447 state |= QStyle::State_Active;
2448 if (d->clickableSections) {
2449 if (logicalIndex == d->hover)
2450 state |= QStyle::State_MouseOver;
2451 if (logicalIndex == d->pressed)
2452 state |= QStyle::State_Sunken;
2453 else if (d->highlightSelected) {
2454 if (d->sectionIntersectsSelection(logicalIndex))
2455 state |= QStyle::State_On;
2456 if (d->isSectionSelected(logicalIndex))
2457 state |= QStyle::State_Sunken;
2458 }
2459
2460 }
2461 if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
2462 opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
2463 ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
2464
2465 // setup the style options structure
2466 QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
2467 Qt::TextAlignmentRole);
2468 opt.rect = rect;
2469 opt.section = logicalIndex;
2470 opt.state |= state;
2471 opt.textAlignment = Qt::Alignment(textAlignment.isValid()
2472 ? Qt::Alignment(textAlignment.toInt())
2473 : d->defaultAlignment);
2474
2475 opt.iconAlignment = Qt::AlignVCenter;
2476 opt.text = d->model->headerData(logicalIndex, d->orientation,
2477 Qt::DisplayRole).toString();
2478 if (d->textElideMode != Qt::ElideNone)
2479 opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);
2480
2481 QVariant variant = d->model->headerData(logicalIndex, d->orientation,
2482 Qt::DecorationRole);
2483 opt.icon = qvariant_cast<QIcon>(variant);
2484 if (opt.icon.isNull())
2485 opt.icon = qvariant_cast<QPixmap>(variant);
2486 QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
2487 Qt::ForegroundRole);
2488 if (qVariantCanConvert<QBrush>(foregroundBrush))
2489 opt.palette.setBrush(QPalette::ButtonText, qvariant_cast<QBrush>(foregroundBrush));
2490
2491 QPointF oldBO = painter->brushOrigin();
2492 QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
2493 Qt::BackgroundRole);
2494 if (qVariantCanConvert<QBrush>(backgroundBrush)) {
2495 opt.palette.setBrush(QPalette::Button, qvariant_cast<QBrush>(backgroundBrush));
2496 opt.palette.setBrush(QPalette::Window, qvariant_cast<QBrush>(backgroundBrush));
2497 painter->setBrushOrigin(opt.rect.topLeft());
2498 }
2499
2500 // the section position
2501 int visual = visualIndex(logicalIndex);
2502 Q_ASSERT(visual != -1);
2503 if (count() == 1)
2504 opt.position = QStyleOptionHeader::OnlyOneSection;
2505 else if (visual == 0)
2506 opt.position = QStyleOptionHeader::Beginning;
2507 else if (visual == count() - 1)
2508 opt.position = QStyleOptionHeader::End;
2509 else
2510 opt.position = QStyleOptionHeader::Middle;
2511 opt.orientation = d->orientation;
2512 // the selected position
2513 bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
2514 bool nextSelected = d->isSectionSelected(this->logicalIndex(visual + 1));
2515 if (previousSelected && nextSelected)
2516 opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
2517 else if (previousSelected)
2518 opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
2519 else if (nextSelected)
2520 opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
2521 else
2522 opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
2523 // draw the section
2524 style()->drawControl(QStyle::CE_Header, &opt, painter, this);
2525
2526 painter->setBrushOrigin(oldBO);
2527}
2528
2529/*!
2530 Returns the size of the contents of the section specified by the given
2531 \a logicalIndex.
2532
2533 \sa defaultSectionSize()
2534*/
2535
2536QSize QHeaderView::sectionSizeFromContents(int logicalIndex) const
2537{
2538 Q_D(const QHeaderView);
2539 Q_ASSERT(logicalIndex >= 0);
2540
2541 ensurePolished();
2542
2543 // use SizeHintRole
2544 QVariant variant = d->model->headerData(logicalIndex, d->orientation, Qt::SizeHintRole);
2545 if (variant.isValid())
2546 return qvariant_cast<QSize>(variant);
2547
2548 // otherwise use the contents
2549 QStyleOptionHeader opt;
2550 initStyleOption(&opt);
2551 opt.section = logicalIndex;
2552 QVariant var = d->model->headerData(logicalIndex, d->orientation,
2553 Qt::FontRole);
2554 QFont fnt;
2555 if (var.isValid() && qVariantCanConvert<QFont>(var))
2556 fnt = qvariant_cast<QFont>(var);
2557 else
2558 fnt = font();
2559 fnt.setBold(true);
2560 opt.fontMetrics = QFontMetrics(fnt);
2561 opt.text = d->model->headerData(logicalIndex, d->orientation,
2562 Qt::DisplayRole).toString();
2563 variant = d->model->headerData(logicalIndex, d->orientation, Qt::DecorationRole);
2564 opt.icon = qvariant_cast<QIcon>(variant);
2565 if (opt.icon.isNull())
2566 opt.icon = qvariant_cast<QPixmap>(variant);
2567 QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this);
2568 if (isSortIndicatorShown()) {
2569 int margin = style()->pixelMetric(QStyle::PM_HeaderMargin, &opt, this);
2570 if (d->orientation == Qt::Horizontal)
2571 size.rwidth() += size.height() + margin;
2572 else
2573 size.rheight() += size.width() + margin;
2574 }
2575 return size;
2576}
2577
2578/*!
2579 Returns the horizontal offset of the header. This is 0 for vertical
2580 headers.
2581
2582 \sa offset()
2583*/
2584
2585int QHeaderView::horizontalOffset() const
2586{
2587 Q_D(const QHeaderView);
2588 if (d->orientation == Qt::Horizontal)
2589 return d->offset;
2590 return 0;
2591}
2592
2593/*!
2594 Returns the vertical offset of the header. This is 0 for horizontal
2595 headers.
2596
2597 \sa offset()
2598*/
2599
2600int QHeaderView::verticalOffset() const
2601{
2602 Q_D(const QHeaderView);
2603 if (d->orientation == Qt::Vertical)
2604 return d->offset;
2605 return 0;
2606}
2607
2608/*!
2609 \reimp
2610 \internal
2611*/
2612
2613void QHeaderView::updateGeometries()
2614{
2615 Q_D(QHeaderView);
2616 d->layoutChildren();
2617 if (d->hasAutoResizeSections())
2618 d->doDelayedResizeSections();
2619}
2620
2621/*!
2622 \reimp
2623 \internal
2624*/
2625
2626void QHeaderView::scrollContentsBy(int dx, int dy)
2627{
2628 Q_D(QHeaderView);
2629 d->scrollDirtyRegion(dx, dy);
2630}
2631
2632/*!
2633 \reimp
2634 \internal
2635*/
2636void QHeaderView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
2637{
2638 Q_D(QHeaderView);
2639 d->invalidateCachedSizeHint();
2640 if (d->hasAutoResizeSections()) {
2641 bool resizeRequired = d->globalResizeMode == ResizeToContents;
2642 int first = orientation() == Qt::Horizontal ? topLeft.column() : topLeft.row();
2643 int last = orientation() == Qt::Horizontal ? bottomRight.column() : bottomRight.row();
2644 for (int i = first; i <= last && !resizeRequired; ++i)
2645 resizeRequired = (resizeMode(i) == ResizeToContents);
2646 if (resizeRequired)
2647 d->doDelayedResizeSections();
2648 }
2649}
2650
2651/*!
2652 \reimp
2653 \internal
2654
2655 Empty implementation because the header doesn't show QModelIndex items.
2656*/
2657void QHeaderView::rowsInserted(const QModelIndex &, int, int)
2658{
2659 // do nothing
2660}
2661
2662/*!
2663 \reimp
2664 \internal
2665
2666 Empty implementation because the header doesn't show QModelIndex items.
2667*/
2668
2669QRect QHeaderView::visualRect(const QModelIndex &) const
2670{
2671 return QRect();
2672}
2673
2674/*!
2675 \reimp
2676 \internal
2677
2678 Empty implementation because the header doesn't show QModelIndex items.
2679*/
2680
2681void QHeaderView::scrollTo(const QModelIndex &, ScrollHint)
2682{
2683 // do nothing - the header only displays sections
2684}
2685
2686/*!
2687 \reimp
2688 \internal
2689
2690 Empty implementation because the header doesn't show QModelIndex items.
2691*/
2692
2693QModelIndex QHeaderView::indexAt(const QPoint &) const
2694{
2695 return QModelIndex();
2696}
2697
2698/*!
2699 \reimp
2700 \internal
2701
2702 Empty implementation because the header doesn't show QModelIndex items.
2703*/
2704
2705bool QHeaderView::isIndexHidden(const QModelIndex &) const
2706{
2707 return true; // the header view has no items, just sections
2708}
2709
2710/*!
2711 \reimp
2712 \internal
2713
2714 Empty implementation because the header doesn't show QModelIndex items.
2715*/
2716
2717QModelIndex QHeaderView::moveCursor(CursorAction, Qt::KeyboardModifiers)
2718{
2719 return QModelIndex();
2720}
2721
2722/*!
2723 \reimp
2724
2725 Selects the items in the given \a rect according to the specified
2726 \a flags.
2727
2728 The base class implementation does nothing.
2729*/
2730
2731void QHeaderView::setSelection(const QRect&, QItemSelectionModel::SelectionFlags)
2732{
2733 // do nothing
2734}
2735
2736/*!
2737 \internal
2738*/
2739
2740QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) const
2741{
2742 Q_D(const QHeaderView);
2743 const int max = d->modelSectionCount();
2744 if (d->orientation == Qt::Horizontal) {
2745 int left = max;
2746 int right = 0;
2747 int rangeLeft, rangeRight;
2748
2749 for (int i = 0; i < selection.count(); ++i) {
2750 QItemSelectionRange r = selection.at(i);
2751 if (r.parent().isValid() || !r.isValid())
2752 continue; // we only know about toplevel items and we don't want invalid ranges
2753 // FIXME an item inside the range may be the leftmost or rightmost
2754 rangeLeft = visualIndex(r.left());
2755 if (rangeLeft == -1) // in some cases users may change the selections
2756 continue; // before we have a chance to do the layout
2757 rangeRight = visualIndex(r.right());
2758 if (rangeRight == -1) // in some cases users may change the selections
2759 continue; // before we have a chance to do the layout
2760 if (rangeLeft < left)
2761 left = rangeLeft;
2762 if (rangeRight > right)
2763 right = rangeRight;
2764 }
2765
2766 int logicalLeft = logicalIndex(left);
2767 int logicalRight = logicalIndex(right);
2768
2769 if (logicalLeft < 0 || logicalLeft >= count() ||
2770 logicalRight < 0 || logicalRight >= count())
2771 return QRegion();
2772
2773 int leftPos = sectionViewportPosition(logicalLeft);
2774 int rightPos = sectionViewportPosition(logicalRight);
2775 rightPos += sectionSize(logicalRight);
2776 return QRect(leftPos, 0, rightPos - leftPos, height());
2777 }
2778 // orientation() == Qt::Vertical
2779 int top = max;
2780 int bottom = 0;
2781 int rangeTop, rangeBottom;
2782
2783 for (int i = 0; i < selection.count(); ++i) {
2784 QItemSelectionRange r = selection.at(i);
2785 if (r.parent().isValid() || !r.isValid())
2786 continue; // we only know about toplevel items
2787 // FIXME an item inside the range may be the leftmost or rightmost
2788 rangeTop = visualIndex(r.top());
2789 if (rangeTop == -1) // in some cases users may change the selections
2790 continue; // before we have a chance to do the layout
2791 rangeBottom = visualIndex(r.bottom());
2792 if (rangeBottom == -1) // in some cases users may change the selections
2793 continue; // before we have a chance to do the layout
2794 if (rangeTop < top)
2795 top = rangeTop;
2796 if (rangeBottom > bottom)
2797 bottom = rangeBottom;
2798 }
2799
2800 int logicalTop = logicalIndex(top);
2801 int logicalBottom = logicalIndex(bottom);
2802
2803 if (logicalTop == -1 || logicalBottom == -1)
2804 return QRect();
2805
2806 int topPos = sectionViewportPosition(logicalTop);
2807 int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom);
2808
2809 return QRect(0, topPos, width(), bottomPos - topPos);
2810}
2811
2812
2813// private implementation
2814
2815int QHeaderViewPrivate::sectionHandleAt(int position)
2816{
2817 Q_Q(QHeaderView);
2818 int visual = q->visualIndexAt(position);
2819 if (visual == -1)
2820 return -1;
2821 int log = logicalIndex(visual);
2822 int pos = q->sectionViewportPosition(log);
2823 int grip = q->style()->pixelMetric(QStyle::PM_HeaderGripMargin, 0, q);
2824
2825 bool atLeft = position < pos + grip;
2826 bool atRight = (position > pos + q->sectionSize(log) - grip);
2827 if (reverse())
2828 qSwap(atLeft, atRight);
2829
2830 if (atLeft) {
2831 //grip at the beginning of the section
2832 while(visual > -1) {
2833 int logical = q->logicalIndex(--visual);
2834 if (!q->isSectionHidden(logical))
2835 return logical;
2836 }
2837 } else if (atRight) {
2838 //grip at the end of the section
2839 return log;
2840 }
2841 return -1;
2842}
2843
2844void QHeaderViewPrivate::setupSectionIndicator(int section, int position)
2845{
2846 Q_Q(QHeaderView);
2847 if (!sectionIndicator) {
2848 sectionIndicator = new QLabel(viewport);
2849 }
2850
2851 int w, h;
2852 int p = q->sectionViewportPosition(section);
2853 if (orientation == Qt::Horizontal) {
2854 w = q->sectionSize(section);
2855 h = viewport->height();
2856 } else {
2857 w = viewport->width();
2858 h = q->sectionSize(section);
2859 }
2860 sectionIndicator->resize(w, h);
2861
2862 QPixmap pm(w, h);
2863 pm.fill(QColor(0, 0, 0, 45));
2864 QRect rect(0, 0, w, h);
2865
2866 QPainter painter(&pm);
2867 painter.setOpacity(0.75);
2868 q->paintSection(&painter, rect, section);
2869 painter.end();
2870
2871 sectionIndicator->setPixmap(pm);
2872 sectionIndicatorOffset = position - qMax(p, 0);
2873}
2874
2875void QHeaderViewPrivate::updateSectionIndicator(int section, int position)
2876{
2877 if (!sectionIndicator)
2878 return;
2879
2880 if (section == -1 || target == -1) {
2881 sectionIndicator->hide();
2882 return;
2883 }
2884
2885 if (orientation == Qt::Horizontal)
2886 sectionIndicator->move(position - sectionIndicatorOffset, 0);
2887 else
2888 sectionIndicator->move(0, position - sectionIndicatorOffset);
2889
2890 sectionIndicator->show();
2891}
2892
2893/*!
2894 Initialize \a option with the values from this QHeaderView. This method is
2895 useful for subclasses when they need a QStyleOptionHeader, but do not want
2896 to fill in all the information themselves.
2897
2898 \sa QStyleOption::initFrom()
2899*/
2900void QHeaderView::initStyleOption(QStyleOptionHeader *option) const
2901{
2902 Q_D(const QHeaderView);
2903 option->initFrom(this);
2904 option->state = QStyle::State_None | QStyle::State_Raised;
2905 option->orientation = d->orientation;
2906 if (d->orientation == Qt::Horizontal)
2907 option->state |= QStyle::State_Horizontal;
2908 if (isEnabled())
2909 option->state |= QStyle::State_Enabled;
2910 option->section = 0;
2911}
2912
2913bool QHeaderViewPrivate::isSectionSelected(int section) const
2914{
2915 int i = section * 2;
2916 if (i < 0 || i >= sectionSelected.count())
2917 return false;
2918 if (sectionSelected.testBit(i)) // if the value was cached
2919 return sectionSelected.testBit(i + 1);
2920 bool s = false;
2921 if (orientation == Qt::Horizontal)
2922 s = isColumnSelected(section);
2923 else
2924 s = isRowSelected(section);
2925 sectionSelected.setBit(i + 1, s); // selection state
2926 sectionSelected.setBit(i, true); // cache state
2927 return s;
2928}
2929
2930/*!
2931 \internal
2932 Returns the last visible (ie. not hidden) visual index
2933*/
2934int QHeaderViewPrivate::lastVisibleVisualIndex() const
2935{
2936 Q_Q(const QHeaderView);
2937 for (int visual = q->count()-1; visual >= 0; --visual) {
2938 if (!q->isSectionHidden(q->logicalIndex(visual)))
2939 return visual;
2940 }
2941
2942 //default value if no section is actually visible
2943 return -1;
2944}
2945
2946/*!
2947 \internal
2948 Go through and resize all of the sections applying stretchLastSection,
2949 manualy stretches, sizes, and useGlobalMode.
2950
2951 The different resize modes are:
2952 Interactive - the user decides the size
2953 Stretch - take up whatever space is left
2954 Fixed - the size is set programmatically outside the header
2955 ResizeToContentes - the size is set based on the contents of the row or column in the parent view
2956
2957 The resize mode will not affect the last section if stretchLastSection is true.
2958*/
2959void QHeaderViewPrivate::resizeSections(QHeaderView::ResizeMode globalMode, bool useGlobalMode)
2960{
2961 Q_Q(QHeaderView);
2962 //stop the timer in case it is delayed
2963 delayedResize.stop();
2964
2965 executePostedLayout();
2966 if (sectionCount == 0)
2967 return;
2968
2969 if (resizeRecursionBlock)
2970 return;
2971 resizeRecursionBlock = true;
2972
2973 invalidateCachedSizeHint();
2974
2975 const int lastVisibleSection = lastVisibleVisualIndex();
2976
2977 // find stretchLastSection if we have it
2978 int stretchSection = -1;
2979 if (stretchLastSection && !useGlobalMode)
2980 stretchSection = lastVisibleVisualIndex();
2981
2982 // count up the number of strected sections and how much space left for them
2983 int lengthToStrech = (orientation == Qt::Horizontal ? viewport->width() : viewport->height());
2984 int numberOfStretchedSections = 0;
2985 QList<int> section_sizes;
2986 for (int i = 0; i < sectionCount; ++i) {
2987 if (isVisualIndexHidden(i))
2988 continue;
2989
2990 QHeaderView::ResizeMode resizeMode;
2991 if (useGlobalMode && (i != stretchSection))
2992 resizeMode = globalMode;
2993 else
2994 resizeMode = (i == stretchSection ? QHeaderView::Stretch : headerSectionResizeMode(i));
2995
2996 if (resizeMode == QHeaderView::Stretch) {
2997 ++numberOfStretchedSections;
2998 section_sizes.append(headerSectionSize(i));
2999 continue;
3000 }
3001
3002 // because it isn't stretch, determine its width and remove that from lengthToStrech
3003 int sectionSize = 0;
3004 if (resizeMode == QHeaderView::Interactive || resizeMode == QHeaderView::Fixed) {
3005 sectionSize = headerSectionSize(i);
3006 } else { // resizeMode == QHeaderView::ResizeToContents
3007 int logicalIndex = q->logicalIndex(i);
3008 sectionSize = qMax(viewSectionSizeHint(logicalIndex),
3009 q->sectionSizeHint(logicalIndex));
3010 }
3011 section_sizes.append(sectionSize);
3012 lengthToStrech -= sectionSize;
3013 }
3014
3015 // calculate the new length for all of the stretched sections
3016 int stretchSectionLength = -1;
3017 int pixelReminder = 0;
3018 if (numberOfStretchedSections > 0 && lengthToStrech > 0) { // we have room to stretch in
3019 int hintLengthForEveryStretchedSection = lengthToStrech / numberOfStretchedSections;
3020 stretchSectionLength = qMax(hintLengthForEveryStretchedSection, q->minimumSectionSize());
3021 pixelReminder = lengthToStrech % numberOfStretchedSections;
3022 }
3023
3024 int spanStartSection = 0;
3025 int previousSectionLength = 0;
3026
3027 QHeaderView::ResizeMode previousSectionResizeMode = QHeaderView::Interactive;
3028
3029 // resize each section along the total length
3030 for (int i = 0; i < sectionCount; ++i) {
3031 int oldSectionLength = headerSectionSize(i);
3032 int newSectionLength = -1;
3033 QHeaderView::ResizeMode newSectionResizeMode = headerSectionResizeMode(i);
3034
3035 if (isVisualIndexHidden(i)) {
3036 newSectionLength = 0;
3037 } else {
3038 QHeaderView::ResizeMode resizeMode;
3039 if (useGlobalMode)
3040 resizeMode = globalMode;
3041 else
3042 resizeMode = (i == stretchSection
3043 ? QHeaderView::Stretch
3044 : newSectionResizeMode);
3045 if (resizeMode == QHeaderView::Stretch && stretchSectionLength != -1) {
3046 if (i == lastVisibleSection)
3047 newSectionLength = qMax(stretchSectionLength, lastSectionSize);
3048 else
3049 newSectionLength = stretchSectionLength;
3050 if (pixelReminder > 0) {
3051 newSectionLength += 1;
3052 --pixelReminder;
3053 }
3054 section_sizes.removeFirst();
3055 } else {
3056 newSectionLength = section_sizes.front();
3057 section_sizes.removeFirst();
3058 }
3059 }
3060
3061 //Q_ASSERT(newSectionLength > 0);
3062 if ((previousSectionResizeMode != newSectionResizeMode
3063 || previousSectionLength != newSectionLength) && i > 0) {
3064 int spanLength = (i - spanStartSection) * previousSectionLength;
3065 createSectionSpan(spanStartSection, i - 1, spanLength, previousSectionResizeMode);
3066 //Q_ASSERT(headerLength() == length);
3067 spanStartSection = i;
3068 }
3069
3070 if (newSectionLength != oldSectionLength)
3071 emit q->sectionResized(logicalIndex(i), oldSectionLength, newSectionLength);
3072
3073 previousSectionLength = newSectionLength;
3074 previousSectionResizeMode = newSectionResizeMode;
3075 }
3076
3077 createSectionSpan(spanStartSection, sectionCount - 1,
3078 (sectionCount - spanStartSection) * previousSectionLength,
3079 previousSectionResizeMode);
3080 //Q_ASSERT(headerLength() == length);
3081 resizeRecursionBlock = false;
3082 viewport->update();
3083}
3084
3085void QHeaderViewPrivate::createSectionSpan(int start, int end, int size, QHeaderView::ResizeMode mode)
3086{
3087 // ### the code for merging spans does not merge at all opertuneties
3088 // ### what if the number of sections is reduced ?
3089
3090 SectionSpan span(size, (end - start) + 1, mode);
3091 int start_section = 0;
3092#ifndef QT_NO_DEBUG
3093 int initial_section_count = headerSectionCount(); // ### debug code
3094#endif
3095
3096 QList<int> spansToRemove;
3097 for (int i = 0; i < sectionSpans.count(); ++i) {
3098 int end_section = start_section + sectionSpans.at(i).count - 1;
3099 int section_count = sectionSpans.at(i).count;
3100 if (start <= start_section && end > end_section) {
3101 // the existing span is entirely coveded by the new span
3102 spansToRemove.append(i);
3103 } else if (start < start_section && end >= end_section) {
3104 // the existing span is entirely coveded by the new span
3105 spansToRemove.append(i);
3106 } else if (start == start_section && end == end_section) {
3107 // the new span is covered by an existin span
3108 length -= sectionSpans.at(i).size;
3109 length += size;
3110 sectionSpans[i].size = size;
3111 sectionSpans[i].resizeMode = mode;
3112 // ### check if we can merge the section with any of its neighbours
3113 removeSpans(spansToRemove);
3114 Q_ASSERT(initial_section_count == headerSectionCount());
3115 return;
3116 } else if (start > start_section && end < end_section) {
3117 if (sectionSpans.at(i).sectionSize() == span.sectionSize()
3118 && sectionSpans.at(i).resizeMode == span.resizeMode) {
3119 Q_ASSERT(initial_section_count == headerSectionCount());
3120 return;
3121 }
3122 // the new span is in the middle of the old span, so we have to split it
3123 length -= sectionSpans.at(i).size;
3124 int section_size = sectionSpans.at(i).sectionSize();
3125#ifndef QT_NO_DEBUG
3126 int span_count = sectionSpans.at(i).count;
3127#endif
3128 QHeaderView::ResizeMode span_mode = sectionSpans.at(i).resizeMode;
3129 // first span
3130 int first_span_count = start - start_section;
3131 int first_span_size = section_size * first_span_count;
3132 sectionSpans[i].count = first_span_count;
3133 sectionSpans[i].size = first_span_size;
3134 sectionSpans[i].resizeMode = span_mode;
3135 length += first_span_size;
3136 // middle span (the new span)
3137#ifndef QT_NO_DEBUG
3138 int mid_span_count = span.count;
3139#endif
3140 int mid_span_size = span.size;
3141 sectionSpans.insert(i + 1, span);
3142 length += mid_span_size;
3143 // last span
3144 int last_span_count = end_section - end;
3145 int last_span_size = section_size * last_span_count;
3146 sectionSpans.insert(i + 2, SectionSpan(last_span_size, last_span_count, span_mode));
3147 length += last_span_size;
3148 Q_ASSERT(span_count == first_span_count + mid_span_count + last_span_count);
3149 removeSpans(spansToRemove);
3150 Q_ASSERT(initial_section_count == headerSectionCount());
3151 return;
3152 } else if (start > start_section && start <= end_section && end >= end_section) {
3153 // the new span covers the last part of the existing span
3154 length -= sectionSpans.at(i).size;
3155 int removed_count = (end_section - start + 1);
3156 int span_count = sectionSpans.at(i).count - removed_count;
3157 int section_size = sectionSpans.at(i).sectionSize();
3158 int span_size = section_size * span_count;
3159 sectionSpans[i].count = span_count;
3160 sectionSpans[i].size = span_size;
3161 length += span_size;
3162 if (end == end_section) {
3163 sectionSpans.insert(i + 1, span); // insert after
3164 length += span.size;
3165 removeSpans(spansToRemove);
3166 Q_ASSERT(initial_section_count == headerSectionCount());
3167 return;
3168 }
3169 } else if (end < end_section && end >= start_section && start <= start_section) {
3170 // the new span covers the first part of the existing span
3171 length -= sectionSpans.at(i).size;
3172 int removed_count = (end - start_section + 1);
3173 int section_size = sectionSpans.at(i).sectionSize();
3174 int span_count = sectionSpans.at(i).count - removed_count;
3175 int span_size = section_size * span_count;
3176 sectionSpans[i].count = span_count;
3177 sectionSpans[i].size = span_size;
3178 length += span_size;
3179 sectionSpans.insert(i, span); // insert before
3180 length += span.size;
3181 removeSpans(spansToRemove);
3182 Q_ASSERT(initial_section_count == headerSectionCount());
3183 return;
3184 }
3185 start_section += section_count;
3186 }
3187
3188 // ### adding and removing _ sections_ in addition to spans
3189 // ### add some more checks here
3190
3191 if (spansToRemove.isEmpty()) {
3192 if (!sectionSpans.isEmpty()
3193 && sectionSpans.last().sectionSize() == span.sectionSize()
3194 && sectionSpans.last().resizeMode == span.resizeMode) {
3195 length += span.size;
3196 int last = sectionSpans.count() - 1;
3197 sectionSpans[last].count += span.count;
3198 sectionSpans[last].size += span.size;
3199 sectionSpans[last].resizeMode = span.resizeMode;
3200 } else {
3201 length += span.size;
3202 sectionSpans.append(span);
3203 }
3204 } else {
3205 removeSpans(spansToRemove);
3206 length += span.size;
3207 sectionSpans.insert(spansToRemove.first(), span);
3208 //Q_ASSERT(initial_section_count == headerSectionCount());
3209 }
3210}
3211
3212void QHeaderViewPrivate::removeSectionsFromSpans(int start, int end)
3213{
3214 // remove sections
3215 int start_section = 0;
3216 QList<int> spansToRemove;
3217 for (int i = 0; i < sectionSpans.count(); ++i) {
3218 int end_section = start_section + sectionSpans.at(i).count - 1;
3219 int section_size = sectionSpans.at(i).sectionSize();
3220 int section_count = sectionSpans.at(i).count;
3221 if (start <= start_section && end >= end_section) {
3222 // the change covers the entire span
3223 spansToRemove.append(i);
3224 if (end == end_section)
3225 break;
3226 } else if (start > start_section && end < end_section) {
3227 // all the removed sections are inside the span
3228 int change = (end - start + 1);
3229 sectionSpans[i].count -= change;
3230 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3231 length -= (change * section_size);
3232 break;
3233 } else if (start >= start_section && start <= end_section) {
3234 // the some of the removed sections are inside the span,at the end
3235 int change = qMin(end_section - start + 1, end - start + 1);
3236 sectionSpans[i].count -= change;
3237 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3238 start += change;
3239 length -= (change * section_size);
3240 // the change affects several spans
3241 } else if (end >= start_section && end <= end_section) {
3242 // the some of the removed sections are inside the span, at the beginning
3243 int change = qMin((end - start_section + 1), end - start + 1);
3244 sectionSpans[i].count -= change;
3245 sectionSpans[i].size = section_size * sectionSpans.at(i).count;
3246 length -= (change * section_size);
3247 break;
3248 }
3249 start_section += section_count;
3250 }
3251
3252 for (int i = spansToRemove.count() - 1; i >= 0; --i) {
3253 int s = spansToRemove.at(i);
3254 length -= sectionSpans.at(s).size;
3255 sectionSpans.remove(s);
3256 // ### merge remaining spans
3257 }
3258}
3259
3260void QHeaderViewPrivate::clear()
3261{
3262 if (state != NoClear) {
3263 length = 0;
3264 sectionCount = 0;
3265 visualIndices.clear();
3266 logicalIndices.clear();
3267 sectionSelected.clear();
3268 sectionHidden.clear();
3269 hiddenSectionSize.clear();
3270 sectionSpans.clear();
3271 }
3272}
3273
3274void QHeaderViewPrivate::flipSortIndicator(int section)
3275{
3276 Q_Q(QHeaderView);
3277 bool ascending = (sortIndicatorSection != section
3278 || sortIndicatorOrder == Qt::DescendingOrder);
3279 q->setSortIndicator(section, ascending ? Qt::AscendingOrder : Qt::DescendingOrder);
3280}
3281
3282void QHeaderViewPrivate::cascadingResize(int visual, int newSize)
3283{
3284 Q_Q(QHeaderView);
3285 const int minimumSize = q->minimumSectionSize();
3286 const int oldSize = headerSectionSize(visual);
3287 int delta = newSize - oldSize;
3288
3289 if (delta > 0) { // larger
3290 bool sectionResized = false;
3291
3292 // restore old section sizes
3293 for (int i = firstCascadingSection; i < visual; ++i) {
3294 if (cascadingSectionSize.contains(i)) {
3295 int currentSectionSize = headerSectionSize(i);
3296 int originalSectionSize = cascadingSectionSize.value(i);
3297 if (currentSectionSize < originalSectionSize) {
3298 int newSectionSize = currentSectionSize + delta;
3299 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3300 if (newSectionSize >= originalSectionSize && false)
3301 cascadingSectionSize.remove(i); // the section is now restored
3302 sectionResized = true;
3303 break;
3304 }
3305 }
3306
3307 }
3308
3309 // resize the section
3310 if (!sectionResized) {
3311 newSize = qMax(newSize, minimumSize);
3312 if (oldSize != newSize)
3313 resizeSectionSpan(visual, oldSize, newSize);
3314 }
3315
3316 // cascade the section size change
3317 for (int i = visual + 1; i < sectionCount; ++i) {
3318 if (!sectionIsCascadable(i))
3319 continue;
3320 int currentSectionSize = headerSectionSize(i);
3321 if (currentSectionSize <= minimumSize)
3322 continue;
3323 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3324 //qDebug() << "### cascading to" << i << newSectionSize - currentSectionSize << delta;
3325 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3326 saveCascadingSectionSize(i, currentSectionSize);
3327 delta = delta - (currentSectionSize - newSectionSize);
3328 //qDebug() << "new delta" << delta;
3329 //if (newSectionSize != minimumSize)
3330 if (delta <= 0)
3331 break;
3332 }
3333 } else { // smaller
3334 bool sectionResized = false;
3335
3336 // restore old section sizes
3337 for (int i = lastCascadingSection; i > visual; --i) {
3338 if (!cascadingSectionSize.contains(i))
3339 continue;
3340 int currentSectionSize = headerSectionSize(i);
3341 int originalSectionSize = cascadingSectionSize.value(i);
3342 if (currentSectionSize >= originalSectionSize)
3343 continue;
3344 int newSectionSize = currentSectionSize - delta;
3345 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3346 if (newSectionSize >= originalSectionSize && false) {
3347 //qDebug() << "section" << i << "restored to" << originalSectionSize;
3348 cascadingSectionSize.remove(i); // the section is now restored
3349 }
3350 sectionResized = true;
3351 break;
3352 }
3353
3354 // resize the section
3355 resizeSectionSpan(visual, oldSize, qMax(newSize, minimumSize));
3356
3357 // cascade the section size change
3358 if (delta < 0 && newSize < minimumSize) {
3359 for (int i = visual - 1; i >= 0; --i) {
3360 if (!sectionIsCascadable(i))
3361 continue;
3362 int sectionSize = headerSectionSize(i);
3363 if (sectionSize <= minimumSize)
3364 continue;
3365 resizeSectionSpan(i, sectionSize, qMax(sectionSize + delta, minimumSize));
3366 saveCascadingSectionSize(i, sectionSize);
3367 break;
3368 }
3369 }
3370
3371 // let the next section get the space from the resized section
3372 if (!sectionResized) {
3373 for (int i = visual + 1; i < sectionCount; ++i) {
3374 if (!sectionIsCascadable(i))
3375 continue;
3376 int currentSectionSize = headerSectionSize(i);
3377 int newSectionSize = qMax(currentSectionSize - delta, minimumSize);
3378 resizeSectionSpan(i, currentSectionSize, newSectionSize);
3379 break;
3380 }
3381 }
3382 }
3383
3384 if (hasAutoResizeSections())
3385 doDelayedResizeSections();
3386
3387 viewport->update();
3388}
3389
3390void QHeaderViewPrivate::setDefaultSectionSize(int size)
3391{
3392 Q_Q(QHeaderView);
3393 defaultSectionSize = size;
3394 int currentVisualIndex = 0;
3395 for (int i = 0; i < sectionSpans.count(); ++i) {
3396 QHeaderViewPrivate::SectionSpan &span = sectionSpans[i];
3397 if (span.size > 0) {
3398 //we resize it if it is not hidden (ie size > 0)
3399 const int newSize = span.count * size;
3400 if (newSize != span.size) {
3401 length += newSize - span.size; //the whole length is changed
3402 const int oldSectionSize = span.sectionSize();
3403 span.size = span.count * size;
3404 for (int i = currentVisualIndex; i < currentVisualIndex + span.count; ++i) {
3405 emit q->sectionResized(logicalIndex(i), oldSectionSize, size);
3406 }
3407 }
3408 }
3409 currentVisualIndex += span.count;
3410 }
3411}
3412
3413void QHeaderViewPrivate::resizeSectionSpan(int visualIndex, int oldSize, int newSize)
3414{
3415 Q_Q(QHeaderView);
3416 QHeaderView::ResizeMode mode = headerSectionResizeMode(visualIndex);
3417 createSectionSpan(visualIndex, visualIndex, newSize, mode);
3418 emit q->sectionResized(logicalIndex(visualIndex), oldSize, newSize);
3419}
3420
3421int QHeaderViewPrivate::headerSectionSize(int visual) const
3422{
3423 // ### stupid iteration
3424 int section_start = 0;
3425 const int sectionSpansCount = sectionSpans.count();
3426 for (int i = 0; i < sectionSpansCount; ++i) {
3427 const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3428 int section_end = section_start + currentSection.count - 1;
3429 if (visual >= section_start && visual <= section_end)
3430 return currentSection.sectionSize();
3431 section_start = section_end + 1;
3432 }
3433 return -1;
3434}
3435
3436int QHeaderViewPrivate::headerSectionPosition(int visual) const
3437{
3438 // ### stupid iteration
3439 int section_start = 0;
3440 int span_position = 0;
3441 const int sectionSpansCount = sectionSpans.count();
3442 for (int i = 0; i < sectionSpansCount; ++i) {
3443 const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3444 int section_end = section_start + currentSection.count - 1;
3445 if (visual >= section_start && visual <= section_end)
3446 return span_position + (visual - section_start) * currentSection.sectionSize();
3447 section_start = section_end + 1;
3448 span_position += currentSection.size;
3449 }
3450 return -1;
3451}
3452
3453int QHeaderViewPrivate::headerVisualIndexAt(int position) const
3454{
3455 // ### stupid iteration
3456 int span_start_section = 0;
3457 int span_position = 0;
3458 const int sectionSpansCount = sectionSpans.count();
3459 for (int i = 0; i < sectionSpansCount; ++i) {
3460 const QHeaderViewPrivate::SectionSpan &currentSection = sectionSpans.at(i);
3461 int next_span_start_section = span_start_section + currentSection.count;
3462 int next_span_position = span_position + currentSection.size;
3463 if (position == span_position)
3464 return span_start_section; // spans with no size
3465 if (position > span_position && position < next_span_position) {
3466 int position_in_span = position - span_position;
3467 return span_start_section + (position_in_span / currentSection.sectionSize());
3468 }
3469 span_start_section = next_span_start_section;
3470 span_position = next_span_position;
3471 }
3472 return -1;
3473}
3474
3475void QHeaderViewPrivate::setHeaderSectionResizeMode(int visual, QHeaderView::ResizeMode mode)
3476{
3477 int size = headerSectionSize(visual);
3478 createSectionSpan(visual, visual, size, mode);
3479}
3480
3481QHeaderView::ResizeMode QHeaderViewPrivate::headerSectionResizeMode(int visual) const
3482{
3483 int span = sectionSpanIndex(visual);
3484 if (span == -1)
3485 return globalResizeMode;
3486 return sectionSpans.at(span).resizeMode;
3487}
3488
3489void QHeaderViewPrivate::setGlobalHeaderResizeMode(QHeaderView::ResizeMode mode)
3490{
3491 globalResizeMode = mode;
3492 for (int i = 0; i < sectionSpans.count(); ++i)
3493 sectionSpans[i].resizeMode = mode;
3494}
3495
3496int QHeaderViewPrivate::viewSectionSizeHint(int logical) const
3497{
3498 if (QAbstractItemView *view = qobject_cast<QAbstractItemView*>(parent)) {
3499 return (orientation == Qt::Horizontal
3500 ? view->sizeHintForColumn(logical)
3501 : view->sizeHintForRow(logical));
3502 }
3503 return 0;
3504}
3505
3506int QHeaderViewPrivate::adjustedVisualIndex(int visualIndex) const
3507{
3508 if (hiddenSectionSize.count() > 0) {
3509 int adjustedVisualIndex = visualIndex;
3510 int currentVisualIndex = 0;
3511 for (int i = 0; i < sectionSpans.count(); ++i) {
3512 if (sectionSpans.at(i).size == 0)
3513 adjustedVisualIndex += sectionSpans.at(i).count;
3514 else
3515 currentVisualIndex += sectionSpans.at(i).count;
3516 if (currentVisualIndex >= visualIndex)
3517 break;
3518 }
3519 visualIndex = adjustedVisualIndex;
3520 }
3521 return visualIndex;
3522}
3523
3524#ifndef QT_NO_DATASTREAM
3525void QHeaderViewPrivate::write(QDataStream &out) const
3526{
3527 out << int(orientation);
3528 out << int(sortIndicatorOrder);
3529 out << sortIndicatorSection;
3530 out << sortIndicatorShown;
3531
3532 out << visualIndices;
3533 out << logicalIndices;
3534
3535 out << sectionHidden;
3536 out << hiddenSectionSize;
3537
3538 out << length;
3539 out << sectionCount;
3540 out << movableSections;
3541 out << clickableSections;
3542 out << highlightSelected;
3543 out << stretchLastSection;
3544 out << cascadingResizing;
3545 out << stretchSections;
3546 out << contentsSections;
3547 out << defaultSectionSize;
3548 out << minimumSectionSize;
3549
3550 out << int(defaultAlignment);
3551 out << int(globalResizeMode);
3552
3553 out << sectionSpans;
3554}
3555
3556bool QHeaderViewPrivate::read(QDataStream &in)
3557{
3558 int orient, order, align, global;
3559 in >> orient;
3560 orientation = (Qt::Orientation)orient;
3561
3562 in >> order;
3563 sortIndicatorOrder = (Qt::SortOrder)order;
3564
3565 in >> sortIndicatorSection;
3566 in >> sortIndicatorShown;
3567
3568 in >> visualIndices;
3569 in >> logicalIndices;
3570
3571 in >> sectionHidden;
3572 in >> hiddenSectionSize;
3573
3574 in >> length;
3575 in >> sectionCount;
3576 in >> movableSections;
3577 in >> clickableSections;
3578 in >> highlightSelected;
3579 in >> stretchLastSection;
3580 in >> cascadingResizing;
3581 in >> stretchSections;
3582 in >> contentsSections;
3583 in >> defaultSectionSize;
3584 in >> minimumSectionSize;
3585
3586 in >> align;
3587 defaultAlignment = Qt::Alignment(align);
3588
3589 in >> global;
3590 globalResizeMode = (QHeaderView::ResizeMode)global;
3591
3592 in >> sectionSpans;
3593
3594 return true;
3595}
3596
3597#endif // QT_NO_DATASTREAM
3598
3599QT_END_NAMESPACE
3600
3601#endif // QT_NO_ITEMVIEWS
3602
3603#include "moc_qheaderview.cpp"
Note: See TracBrowser for help on using the repository browser.