1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the Qt 3 compatibility classes of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
31
#include "qapplication.h"
32
#include "qbitarray.h"
34
#include "qdrawutil.h"
39
#include "qstyleoption.h"
57
for (int i = 0; i < n; i ++) {
64
clicks_default = true;
65
resize_default = true;
66
clicks.fill(clicks_default);
67
resize.fill(resize_default);
71
positionsDirty = true;
75
is_a_table_header = false;
80
for (int i = 0; i < icons.size(); ++i)
86
int height; // we abuse the heights as widths for vertical layout
88
QVector<int> positions; // sorted by index
89
QVector<QString> labels;
90
QVector<QIcon *> icons;
97
uint clicks_default : 1; // default value for new clicks bits
98
uint resize_default : 1; // default value for new resize bits
100
uint is_a_table_header : 1;
110
int sectionAt(int pos) {
111
// positions is sorted by index, not by section
116
int i = ((l+r+1) / 2);
118
if (positions[i] > pos)
124
if (positions[i] <= pos && pos <= positions[i] + sizes[i2s[i]])
130
static QStyleOptionHeader getStyleOption(const Q3Header *header, int section)
132
QStyleOptionHeader opt;
134
opt.section = section;
135
if (header->iconSet(section))
136
opt.icon = *header->iconSet(section);
137
opt.text = header->label(section);
138
if (header->orientation() == Qt::Horizontal)
139
opt.state = QStyle::State_Horizontal;
144
\class Q3Header q3header.h
145
\brief The Q3Header class provides a header row or column, e.g. for
146
tables and listviews.
150
This class provides a header, e.g. a vertical header to display
151
row labels, or a horizontal header to display column labels. It is
152
used by Q3Table and Q3ListView for example.
154
A header is composed of one or more \e sections, each of which can
155
display a text label and an \link QIcon icon\endlink. A sort
156
indicator (an arrow) can also be displayed using
159
Sections are added with addLabel() and removed with removeLabel().
160
The label and icon are set in addLabel() and can be changed
161
later with setLabel(). Use count() to retrieve the number of
162
sections in the header.
164
The orientation of the header is set with setOrientation(). If
165
setStretchEnabled() is true, the sections will expand to take up
166
the full width (height for vertical headers) of the header. The
167
user can resize the sections manually if setResizeEnabled() is
168
true. Call adjustHeaderSize() to have the sections resize to
169
occupy the full width (or height).
171
A section can be moved with moveSection(). If setMovingEnabled()
172
is true (the default)the user may drag a section from one position
173
to another. If a section is moved, the index positions at which
174
sections were added (with addLabel()), may not be the same after the
175
move. You don't have to worry about this in practice because the
176
Q3Header API works in terms of section numbers, so it doesn't matter
177
where a particular section has been moved to.
179
If you want the current index position of a section call
180
mapToIndex() giving it the section number. (This is the number
181
returned by the addLabel() call which created the section.) If you
182
want to get the section number of a section at a particular index
183
position call mapToSection() giving it the index number.
185
Here's an example to clarify mapToSection() and mapToIndex():
188
\header \i41 Index positions
189
\row \i 0 \i 1 \i 2 \i 3
190
\header \i41 Original section ordering
191
\row \i Sect 0 \i Sect 1 \i Sect 2 \i Sect 3
192
\header \i41 Ordering after the user moves a section
193
\row \i Sect 0 \i Sect 2 \i Sect 3 \i Sect 1
197
\header \i \e k \i mapToSection(\e k) \i mapToIndex(\e k)
204
In the example above, if we wanted to find out which section is at
205
index position 3 we'd call mapToSection(3) and get a section
206
number of 1 since section 1 was moved. Similarly, if we wanted to
207
know which index position section 2 occupied we'd call
208
mapToIndex(2) and get an index of 1.
210
Q3Header provides the clicked(), pressed() and released() signals.
211
If the user changes the size of a section, the sizeChange() signal
212
is emitted. If you want to have a sizeChange() signal emitted
213
continuously whilst the user is resizing (rather than just after
214
the resizing is finished), use setTracking(). If the user moves a
215
section the indexChange() signal is emitted.
217
\sa Q3ListView Q3Table
223
Constructs a horizontal header called \a name, with parent \a
227
Q3Header::Q3Header(QWidget *parent, const char *name)
228
: QWidget(parent, name, Qt::WStaticContents)
230
orient = Qt::Horizontal;
235
Constructs a horizontal header called \a name, with \a n sections
236
and parent \a parent.
239
Q3Header::Q3Header(int n, QWidget *parent, const char *name)
240
: QWidget(parent, name, Qt::WStaticContents)
242
orient = Qt::Horizontal;
247
Destroys the header and all its sections.
250
Q3Header::~Q3Header()
259
void Q3Header::showEvent(QShowEvent *e)
261
calculatePositions();
262
QWidget::showEvent(e);
266
\fn void Q3Header::sizeChange(int section, int oldSize, int newSize)
268
This signal is emitted when the user has changed the size of a \a
269
section from \a oldSize to \a newSize. This signal is typically
270
connected to a slot that repaints the table or list that contains
275
\fn void Q3Header::clicked(int section)
277
If isClickEnabled() is true, this signal is emitted when the user
278
clicks section \a section.
280
\sa pressed(), released()
284
\fn void Q3Header::pressed(int section)
286
This signal is emitted when the user presses section \a section
293
\fn void Q3Header::released(int section)
295
This signal is emitted when section \a section is released.
302
\fn void Q3Header::indexChange(int section, int fromIndex, int toIndex)
304
This signal is emitted when the user moves section \a section from
305
index position \a fromIndex, to index position \a toIndex.
309
\fn void Q3Header::moved(int fromIndex, int toIndex)
311
Use indexChange() instead.
313
This signal is emitted when the user has moved the section which
314
is displayed at the index \a fromIndex to the index \a toIndex.
318
\fn void Q3Header::sectionClicked(int index)
320
Use clicked() instead.
322
This signal is emitted when a part of the header is clicked. \a
323
index is the index at which the section is displayed.
325
In a list view this signal would typically be connected to a slot
326
that sorts the specified column (or row).
329
/*! \fn int Q3Header::cellSize(int) const
331
Use sectionSize() instead.
333
Returns the size in pixels of the section that is displayed at
338
\fn void Q3Header::sectionHandleDoubleClicked(int section)
340
This signal is emitted when the user doubleclicks on the edge
341
(handle) of section \a section.
346
Use sectionPos() instead.
348
Returns the position in pixels of the section that is displayed at the
349
index \a i. The position is measured from the start of the header.
352
int Q3Header::cellPos(int i) const
354
if (i == count() && i > 0)
355
return d->positions[i-1] + d->sizes[d->i2s[i-1]]; // compatibility
356
return sectionPos(mapToSection(i));
361
\property Q3Header::count
362
\brief the number of sections in the header
365
int Q3Header::count() const
372
\property Q3Header::tracking
373
\brief whether the sizeChange() signal is emitted continuously
375
If tracking is on, the sizeChange() signal is emitted continuously
376
while the mouse is moved (i.e. when the header is resized),
377
otherwise it is only emitted when the mouse button is released at
380
Tracking defaults to false.
385
Initializes with \a n columns.
387
void Q3Header::init(int n)
390
cachedPos = 0; // unused
391
d = new Q3HeaderData(n);
393
d->heightDirty = true;
396
offs = d->lastPos - width();
397
oldHandleIdx = oldHIdxSize = handleIdx = 0;
399
setMouseTracking(true);
400
trackingIsOn = false;
401
setBackgroundRole(QPalette::Button);
402
setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
406
\property Q3Header::orientation
407
\brief the header's orientation
409
The orientation is either \c Qt::Vertical or \c Qt::Horizontal (the
412
Call setOrientation() before adding labels if you don't provide a
413
size parameter otherwise the sizes will be incorrect.
416
void Q3Header::setOrientation(Qt::Orientation orientation)
418
if (orient == orientation)
420
orient = orientation;
421
if (orient == Qt::Horizontal)
422
setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
424
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
431
Paints a rectangle starting at \a p, with length \s.
433
void Q3Header::paintRect(int p, int s)
435
QPainter paint(this);
436
paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
438
paint.drawRect(p - s, 3, s, height() - 5);
439
else if (orient == Qt::Horizontal)
440
paint.drawRect(p, 3, s, height() - 5);
442
paint.drawRect(3, p, height() - 5, s);
446
Marks the division line at \a idx.
448
void Q3Header::markLine(int idx)
450
QPainter paint(this);
451
paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
452
int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
454
int x = p - MARKSIZE/2;
456
int x2 = p + MARKSIZE/2;
457
int y2 = height() - 3;
458
if (orient == Qt::Vertical) {
459
int t = x; x = y; y = t;
460
t = x2; x2 = y2; y2 = t;
463
paint.drawLine(x, y, x2, y);
464
paint.drawLine(x, y+1, x2, y+1);
466
paint.drawLine(x, y2, x2, y2);
467
paint.drawLine(x, y2-1, x2, y2-1);
469
paint.drawLine(x, y, x, y2);
470
paint.drawLine(x+1, y, x+1, y2);
472
paint.drawLine(x2, y, x2, y2);
473
paint.drawLine(x2-1, y, x2-1, y2);
477
Removes the mark at the division line at \a idx.
479
void Q3Header::unMarkLine(int idx)
483
int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
485
int x = p - MARKSIZE/2;
487
int x2 = p + MARKSIZE/2;
488
int y2 = height() - 3;
489
if (orient == Qt::Vertical) {
490
int t = x; x = y; y = t;
491
t = x2; x2 = y2; y2 = t;
493
repaint(x, y, x2-x+1, y2-y+1);
496
/*! \fn int Q3Header::cellAt(int) const
498
Use sectionAt() instead.
500
Returns the index at which the section is displayed, which contains
501
\a pos in widget coordinates, or -1 if \a pos is outside the header
506
Tries to find a line that is not a neighbor of \c handleIdx.
508
int Q3Header::findLine(int c)
511
if (c > d->lastPos || (reverse() && c < 0)) {
514
int section = sectionAt(c);
519
int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
522
if (i == handleIdx - 1 && pPos(handleIdx) - c > MARKSIZE/2)
524
if (i == handleIdx + 1 && c - pPos(i) > MARKSIZE/2)
526
if (c - pPos(i) > pSize(i) / 2)
533
Returns the handle at position \a p, or -1 if there is no handle at \a p.
535
int Q3Header::handleAt(int p)
537
int section = d->sectionAt(p);
539
int GripMargin = (bool)d->resize[section] ?
540
style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
541
int index = d->s2i[section];
542
if ((index > 0 && p < d->positions[index] + GripMargin) ||
543
(p > d->positions[index] + d->sizes[section] - GripMargin)) {
544
if (index > 0 && p < d->positions[index] + GripMargin)
545
section = d->i2s[--index];
546
// dont show icon if streaching is enabled it is at the end of the last section
547
if (d->resize.testBit(section) && (d->fullSize == -2 || index != count() - 1)) {
557
Use moveSection() instead.
559
Moves the section that is currently displayed at index \a fromIdx
563
void Q3Header::moveCell(int fromIdx, int toIdx)
565
moveSection(mapToSection(fromIdx), toIdx);
571
Move and signal and repaint.
574
void Q3Header::handleColumnMove(int fromIdx, int toIdx)
576
int s = d->i2s[fromIdx];
578
toIdx++; //Convert to
579
QRect r = sRect(fromIdx);
581
moveSection(s, toIdx);
583
emit moved(fromIdx, toIdx);
584
emit indexChange(s, fromIdx, toIdx);
590
void Q3Header::keyPressEvent(QKeyEvent *e)
593
if (e->key() == Qt::Key_Space) {
594
//don't do it if we're doing something with the mouse
595
if (state == Idle && d->clicks[d->i2s[d->focusIdx] ]) {
598
repaint(sRect(handleIdx));
599
emit pressed(d->i2s[i]);
601
} else if (orientation() == Qt::Horizontal &&
602
(e->key() == Qt::Key_Right || e->key() == Qt::Key_Left)
603
|| orientation() == Qt::Vertical &&
604
(e->key() == Qt::Key_Up || e->key() == Qt::Key_Down)) {
605
int dir = e->key() == Qt::Key_Right || e->key() == Qt::Key_Down ? 1 : -1;
607
if (e->state() & Qt::ControlButton && d->resize[s]) {
609
int step = e->state() & Qt::ShiftButton ? dir : 10*dir;
610
int c = d->positions[i] + d->sizes[s] + step;
611
handleColumnResize(i, c, true);
612
} else if (e->state() & (Qt::AltButton|Qt::MetaButton) && d->move) {
614
int i2 = (i + count() + dir) % count();
616
handleColumnMove(i, i2);
618
//focus on different section
619
QRect r = sRect(d->focusIdx);
620
d->focusIdx = (d->focusIdx + count() + dir) % count();
621
r |= sRect(d->focusIdx);
632
void Q3Header::keyReleaseEvent(QKeyEvent *e)
636
//double check that this wasn't started with the mouse
637
if (state == Pressed && handleIdx == d->focusIdx) {
638
repaint(sRect(handleIdx));
639
int section = d->i2s[d->focusIdx];
640
emit released(section);
641
emit sectionClicked(handleIdx);
642
emit clicked(section);
656
void Q3Header::mousePressEvent(QMouseEvent *e)
658
if (e->button() != Qt::LeftButton || state != Idle)
660
oldHIdxSize = handleIdx;
662
int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
667
int section = d->sectionAt(c);
670
int GripMargin = (bool)d->resize[section] ?
671
style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
672
int index = d->s2i[section];
674
if ((index > 0 && c < d->positions[index] + GripMargin) ||
675
(c > d->positions[index] + d->sizes[section] - GripMargin)) {
676
if (c < d->positions[index] + GripMargin)
680
if (d->lastPos <= (orient == Qt::Horizontal ? width() :
681
height()) && d->fullSize != -2 && handleIdx == count() - 1) {
685
oldHIdxSize = d->sizes[d->i2s[handleIdx]];
686
state = d->resize[d->i2s[handleIdx] ] ? Sliding : Blocked;
687
} else if (index >= 0) {
688
oldHandleIdx = handleIdx = index;
690
state = d->clicks[d->i2s[handleIdx] ] ? Pressed : Blocked;
692
repaint(sRect(handleIdx));
693
if(oldHandleIdx != handleIdx)
694
repaint(sRect(oldHandleIdx));
695
emit pressed(section);
698
d->pressDelta = c - (d->positions[handleIdx] + d->sizes[d->i2s[handleIdx]]);
704
void Q3Header::mouseReleaseEvent(QMouseEvent *e)
706
if (e->button() != Qt::LeftButton)
708
int oldOldHandleIdx = oldHandleIdx;
709
State oldState = state;
713
int section = d->i2s[handleIdx];
714
emit released(section);
715
if (sRect(handleIdx).contains(e->pos())) {
716
oldHandleIdx = handleIdx;
717
emit sectionClicked(handleIdx);
718
emit clicked(section);
720
handleIdx = oldHandleIdx;
722
repaint(sRect(handleIdx));
723
if (oldOldHandleIdx != handleIdx)
724
repaint(sRect(oldOldHandleIdx));
727
int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
731
handleColumnResize(handleIdx, c - d->pressDelta, true);
737
int section = d->i2s[handleIdx];
738
if (handleIdx != moveToIdx && moveToIdx != -1) {
739
moveSection(section, moveToIdx);
740
handleIdx = oldHandleIdx;
741
emit moved(handleIdx, moveToIdx);
742
emit indexChange(section, handleIdx, moveToIdx);
743
emit released(section);
744
repaint(); // a bit overkill, but removes the handle as well
746
if (sRect(handleIdx).contains(e->pos())) {
747
oldHandleIdx = handleIdx;
748
emit released(section);
749
emit sectionClicked(handleIdx);
750
emit clicked(section);
752
handleIdx = oldHandleIdx;
754
repaint(sRect(handleIdx));
755
if(oldOldHandleIdx != handleIdx)
756
repaint(sRect(oldOldHandleIdx));
764
// empty, probably. Idle, at any rate.
772
void Q3Header::mouseMoveEvent(QMouseEvent *e)
774
int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
786
else if (orient == Qt::Horizontal)
787
setCursor(Qt::splitHCursor);
789
setCursor(Qt::splitVCursor);
795
if (QABS(c - clickPos) > 4 && d->move) {
799
if (orient == Qt::Horizontal)
800
setCursor(Qt::SizeHorCursor);
802
setCursor(Qt::SizeVerCursor);
807
handleColumnResize(handleIdx, c, false, false);
810
int newPos = findLine(pos);
811
if (newPos != moveToIdx) {
812
if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
813
repaint(sRect(handleIdx));
815
unMarkLine(moveToIdx);
817
if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
818
paintRect(pPos(handleIdx), pSize(handleIdx));
825
qWarning("Q3Header::mouseMoveEvent: (%s) unknown state", objectName().toLocal8Bit().data());
832
void Q3Header::mouseDoubleClickEvent(QMouseEvent *e)
834
int p = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
839
int header = handleAt(p);
841
emit sectionHandleDoubleClicked(header);
845
Handles resizing of sections. This means it redraws the relevant parts
849
void Q3Header::handleColumnResize(int index, int c, bool final, bool recalcAll)
851
int section = d->i2s[index];
852
int GripMargin = (bool)d->resize[section] ?
853
style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
854
int lim = d->positions[index] + 2*GripMargin;
859
int oldSize = d->sizes[section];
860
int newSize = c - d->positions[index];
861
d->sizes[section] = newSize;
863
calculatePositions(!recalcAll, !recalcAll ? section : 0);
865
int pos = d->positions[index]-offset();
866
if(reverse()) // repaint the whole thing. Could be optimized (lars)
867
repaint(0, 0, width(), height());
868
else if (orient == Qt::Horizontal)
869
repaint(pos, 0, width() - pos, height());
871
repaint(0, pos, width(), height() - pos);
874
if (tracking() && oldSize != newSize) {
877
emit sizeChange(section, oldSize, newSize);
878
} else if (!tracking() && final && oldHIdxSize != newSize) {
881
emit sizeChange(section, oldHIdxSize, newSize);
885
if (d->fullSize == -1) {
886
d->fullSize = count() - 1;
889
} else if (d->fullSize >= 0) {
890
int old = d->fullSize;
891
d->fullSize = count() - 1;
899
Returns the rectangle covered by the section at index \a index.
902
QRect Q3Header::sRect(int index)
905
int section = mapToSection(index);
906
if (count() > 0 && index >= count()) {
907
int s = d->positions[count() - 1] - offset() +
908
d->sizes[mapToSection(count() - 1)];
909
if (orient == Qt::Horizontal)
910
return QRect(s, 0, width() - s + 10, height());
912
return QRect(0, s, width(), height() - s + 10);
915
return rect(); // ### eeeeevil
918
return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
919
0, d->sizes[section], height());
920
else if (orient == Qt::Horizontal)
921
return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
923
return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
927
Returns the rectangle covered by section \a section.
930
QRect Q3Header::sectionRect(int section) const
932
int index = mapToIndex(section);
934
return rect(); // ### eeeeevil
937
return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
938
0, d->sizes[section], height());
939
else if (orient == Qt::Horizontal)
940
return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
942
return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
948
Sets the icon for section \a section to \a icon and the text to
949
\a s. The section's width is set to \a size if \a size \>= 0;
950
otherwise it is left unchanged.
952
If the section does not exist, nothing happens.
955
void Q3Header::setLabel(int section, const QIcon& icon,
956
const QString &s, int size)
958
if (section < 0 || section >= count())
960
d->icons.insert(section, new QIcon(icon));
961
setLabel(section, s, size);
965
Sets the text of section \a section to \a s. The section's width
966
is set to \a size if \a size \>= 0; otherwise it is left
967
unchanged. Any icon set that has been set for this section remains
970
If the section does not exist, nothing happens.
972
void Q3Header::setLabel(int section, const QString &s, int size)
974
if (section < 0 || section >= count())
976
d->labels[section] = s;
978
setSectionSizeAndHeight(section, size);
980
if (updatesEnabled()) {
982
calculatePositions();
988
bool qt_qheader_label_return_null_strings = false;
990
Returns the text for section \a section. If the section does not
991
exist, returns an empty string.
993
QString Q3Header::label(int section) const
995
if (section < 0 || section >= count())
997
QString l = d->labels.value(section);
998
if (!l.isNull() || qt_qheader_label_return_null_strings)
1001
return QString::number(section + 1);
1005
Returns the icon set for section \a section. If the section does
1006
not exist, 0 is returned.
1009
QIcon *Q3Header::iconSet(int section) const
1011
if (section < 0 || section >= count())
1013
return d->icons[section];
1020
Adds a new section with icon \a icon and label text \a s.
1021
Returns the index position where the section was added (at the
1022
right for horizontal headers, at the bottom for vertical headers).
1023
The section's width is set to \a size, unless size is negative in
1024
which case the size is calculated taking account of the size of
1027
int Q3Header::addLabel(const QIcon& icon, const QString &s, int size)
1029
int n = count() + 1;
1030
d->icons.resize(n + 1);
1031
d->icons.insert(n - 1, new QIcon(icon));
1032
return addLabel(s, size);
1036
Removes section \a section. If the section does not exist, nothing
1039
void Q3Header::removeLabel(int section)
1041
if (section < 0 || section > count() - 1)
1044
int index = d->s2i[section];
1047
for (i = section; i < n; ++i) {
1048
d->sizes[i] = d->sizes[i+1];
1049
d->labels[i] = d->labels[i+1];
1050
d->labels[i+1] = QString();
1051
d->icons[i] = d->icons[i+1];
1056
d->positions.resize(n);
1057
d->labels.resize(n);
1060
for (i = section; i < n; ++i)
1061
d->s2i[i] = d->s2i[i+1];
1064
if (updatesEnabled()) {
1065
for (i = 0; i < n; ++i)
1066
if (d->s2i[i] > index)
1070
for (i = index; i < n; ++i)
1071
d->i2s[i] = d->i2s[i+1];
1074
if (updatesEnabled()) {
1075
for (i = 0; i < n; ++i)
1076
if (d->i2s[i] > section)
1080
if (updatesEnabled()) {
1082
calculatePositions();
1087
QSize Q3Header::sectionSizeHint(int section, const QFontMetrics& fm) const
1091
if (d->icons[section] != 0) {
1092
QSize isize = d->icons[section]->pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize),
1093
QIcon::Normal).size();
1094
iw = isize.width() + 2;
1095
ih = isize.height();
1099
QString label = d->labels[section];
1100
if (!label.isNull()) {
1101
int lines = label.count('\n') + 1;
1104
bound.setHeight(fm.height() + fm.lineSpacing() * (lines - 1));
1105
QStringList list = label.split('\n');
1106
for (int i=0; i < list.count(); ++i) {
1107
int tmpw = fm.width(list.at(i));
1111
bound.setHeight(fm.height());
1112
w = fm.width(label);
1117
if (d->sortSection == section)
1118
arrowWidth = ((orient == Qt::Horizontal ? height() : width()) / 2) + 8;
1119
int height = qMax(bound.height() + 2, ih) + 4;
1120
int width = bound.width() + style()->pixelMetric(QStyle::PM_HeaderMargin) * 4
1122
return QSize(width, height);
1126
Sets d->sizes[\a section] to a bounding rect based on its size
1127
hint and font metrics, but constrained by \a size. It also updates
1130
void Q3Header::setSectionSizeAndHeight(int section, int size)
1132
QSize sz = sectionSizeHint(section, fontMetrics());
1135
if (d->sizes[section] < 0)
1136
d->sizes[section] = (orient == Qt::Horizontal) ? sz.width()
1139
d->sizes[section] = size;
1142
int newHeight = (orient == Qt::Horizontal) ? sz.height() : sz.width();
1143
if (newHeight > d->height) {
1144
d->height = newHeight;
1145
} else if (newHeight < d->height) {
1147
We could be smarter, but we aren't. This makes a difference
1148
only for users with many columns and '\n's in their headers
1151
d->heightDirty = true;
1156
Adds a new section with label text \a s. Returns the index
1157
position where the section was added (at the right for horizontal
1158
headers, at the bottom for vertical headers). The section's width
1159
is set to \a size. If \a size \< 0, an appropriate size for the
1160
text \a s is chosen.
1162
int Q3Header::addLabel(const QString &s, int size)
1165
if ((int)d->icons.size() < n )
1167
if ((int)d->sizes.size() < n ) {
1168
d->labels.resize(n);
1170
d->positions.resize(n);
1173
d->clicks.resize(n);
1174
d->resize.resize(n);
1176
int section = d->count - 1;
1177
if (!d->is_a_table_header || !s.isNull())
1178
d->labels.insert(section, s);
1180
if (size >= 0 && s.isNull() && d->is_a_table_header) {
1181
d->sizes[section] = size;
1183
d->sizes[section] = -1;
1184
setSectionSizeAndHeight(section, size);
1187
int index = section;
1188
d->positions[index] = d->lastPos;
1190
d->s2i[section] = index;
1191
d->i2s[index] = section;
1192
d->clicks.setBit(section, d->clicks_default);
1193
d->resize.setBit(section, d->resize_default);
1195
if (updatesEnabled()) {
1197
calculatePositions();
1203
void Q3Header::resizeArrays(int size)
1205
d->icons.resize(size);
1206
d->labels.resize(size);
1207
d->sizes.resize(size);
1208
d->positions.resize(size);
1209
d->i2s.resize(size);
1210
d->s2i.resize(size);
1211
d->clicks.resize(size);
1212
d->resize.resize(size);
1215
void Q3Header::setIsATableHeader(bool b)
1217
d->is_a_table_header = b;
1221
QSize Q3Header::sizeHint() const
1227
QFontMetrics fm = fontMetrics();
1229
if (d->heightDirty) {
1230
d->height = fm.lineSpacing() + 6;
1231
for (int i = 0; i < count(); i++) {
1232
int h = orient == Qt::Horizontal ?
1233
sectionSizeHint(i, fm).height() : sectionSizeHint(i, fm).width();
1234
d->height = qMax(d->height, h);
1236
d->heightDirty = false;
1239
if (orient == Qt::Horizontal) {
1240
height = fm.lineSpacing() + 6;
1242
height = qMax(height, d->height);
1243
for (int i = 0; i < count(); i++)
1244
width += d->sizes[i];
1246
width = fm.width(' ');
1248
width = qMax(width, d->height);
1249
for (int i = 0; i < count(); i++)
1250
height += d->sizes[i];
1252
QStyleOptionHeader opt = getStyleOption(this, 0);
1253
return style()->sizeFromContents(QStyle::CT_Q3Header, &opt, QSize(width, height),
1254
this).expandedTo(QApplication::globalStrut());
1258
\property Q3Header::offset
1259
\brief the header's left-most (or top-most) visible pixel
1261
Setting this property will scroll the header so that \e offset
1262
becomes the left-most (or top-most for vertical headers) visible
1265
int Q3Header::offset() const
1268
return d->lastPos - width() - offs;
1272
void Q3Header::setOffset(int x)
1274
int oldOff = offset();
1276
if(d->lastPos < (orient == Qt::Horizontal ? width() : height()))
1279
offs = d->lastPos - width() - x;
1280
if (orient == Qt::Horizontal)
1281
scroll(oldOff-offset(), 0);
1283
scroll(0, oldOff-offset());
1289
Returns the position of actual division line \a i in widget
1290
coordinates. May return a position outside the widget.
1292
Note that the last division line is numbered count(). (There is one
1293
more line than the number of sections).
1295
int Q3Header::pPos(int i) const
1301
pos = d->positions[i];
1303
pos = d->lastPos - pos;
1304
return pos - offset();
1309
Returns the size of the section at index position \a i.
1311
int Q3Header::pSize(int i) const
1313
return d->sizes[d->i2s[i]];
1317
Use mapToSection() instead.
1319
Translates from actual index \a a (index at which the section is displayed) to
1320
logical index of the section. Returns -1 if \a a is outside the legal range.
1325
int Q3Header::mapToLogical(int a) const
1327
return mapToSection(a);
1332
Use mapToIndex() instead.
1334
Translates from logical index \a l to actual index (index at which the section \a l is displayed) .
1335
Returns -1 if \a l is outside the legal range.
1340
int Q3Header::mapToActual(int l) const
1342
return mapToIndex(l);
1347
Use resizeSection() instead.
1349
Sets the size of the section \a section to \a s pixels.
1351
\warning does not repaint or send out signals
1354
void Q3Header::setCellSize(int section, int s)
1356
if (section < 0 || section >= count())
1358
d->sizes[section] = s;
1359
if (updatesEnabled())
1360
calculatePositions();
1365
If \a enable is true the user may resize section \a section;
1366
otherwise the section may not be manually resized.
1368
If \a section is negative (the default) then the \a enable value
1369
is set for all existing sections and will be applied to any new
1370
sections that are added.
1373
// Allow resizing of all current and future sections
1374
header->setResizeEnabled(true);
1375
// Disable resizing of section 3, (the fourth section added)
1376
header->setResizeEnabled(false, 3);
1379
If the user resizes a section, a sizeChange() signal is emitted.
1381
\sa setMovingEnabled() setClickEnabled() setTracking()
1384
void Q3Header::setResizeEnabled(bool enable, int section)
1387
d->resize.fill(enable);
1388
// and future ones...
1389
d->resize_default = enable;
1390
} else if (section < count()) {
1391
d->resize[section] = enable;
1397
\property Q3Header::moving
1398
\brief whether the header sections can be moved
1400
If this property is true (the default) the user can move sections.
1401
If the user moves a section the indexChange() signal is emitted.
1403
\sa setClickEnabled(), setResizeEnabled()
1406
void Q3Header::setMovingEnabled(bool enable)
1413
If \a enable is true, any clicks on section \a section will result
1414
in clicked() signals being emitted; otherwise the section will
1417
If \a section is -1 (the default) then the \a enable value is set
1418
for all existing sections and will be applied to any new sections
1421
\sa setMovingEnabled(), setResizeEnabled()
1424
void Q3Header::setClickEnabled(bool enable, int section)
1427
d->clicks.fill(enable);
1428
// and future ones...
1429
d->clicks_default = enable;
1430
} else if (section < count()) {
1431
d->clicks[section] = enable;
1437
Paints the section at position \a index, inside rectangle \a fr
1438
(which uses widget coordinates) using painter \a p.
1440
Calls paintSectionLabel().
1443
void Q3Header::paintSection(QPainter *p, int index, const QRect& fr)
1445
int section = mapToSection(index);
1446
QStyleOptionHeader opt = getStyleOption(this, section);
1447
opt.state |= QStyle::State_Raised;
1451
style()->drawControl(QStyle::CE_HeaderSection, &opt, p, this);
1455
if (sectionSize(section) <= 0)
1458
opt.state = (orient == Qt::Horizontal ? QStyle::State_Horizontal : QStyle::State_None);
1459
if (d->sortSection == section)
1460
opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
1463
opt.state |= QStyle::State_Enabled;
1464
if (isClickEnabled(section) && (state == Pressed || state == Moving) && index == handleIdx)
1465
opt.state |= QStyle::State_Sunken; //currently pressed
1466
if (!(opt.state & QStyle::State_Sunken))
1467
opt.state |= QStyle::State_Raised;
1468
p->setBrushOrigin(fr.topLeft());
1469
if (d->clicks[section]) {
1470
style()->drawControl(QStyle::CE_HeaderSection, &opt, p, this);
1473
p->setClipRect(fr); // hack to keep styles working
1474
opt.rect.setRect(fr.x() - 2, fr.y() - 2, fr.width() + 4, fr.height() + 4);
1475
style()->drawControl(QStyle::CE_Header, &opt, p, this);
1476
if (orient == Qt::Horizontal) {
1477
p->setPen(palette().color(QPalette::Mid));
1478
p->drawLine(fr.x(), fr.y() + fr.height() - 1,
1479
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1480
p->drawLine(fr.x() + fr.width() - 1, fr.y(),
1481
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1482
p->setPen(palette().color(QPalette::Light));
1484
p->drawLine(fr.x(), fr.y(), fr.x(), fr.y() + fr.height() - 1);
1485
if (index == count() - 1) {
1486
p->drawLine(fr.x() + fr.width() - 1, fr.y(),
1487
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1488
p->setPen(palette().color(QPalette::Mid));
1489
p->drawLine(fr.x() + fr.width() - 2, fr.y(),
1490
fr.x() + fr.width() - 2, fr.y() + fr.height() - 1);
1493
p->setPen(palette().color(QPalette::Mid));
1494
p->drawLine(fr.x() + width() - 1, fr.y(),
1495
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1496
p->drawLine(fr.x(), fr.y() + fr.height() - 1,
1497
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1498
p->setPen(palette().color(QPalette::Light));
1500
p->drawLine(fr.x(), fr.y(), fr.x() + fr.width() - 1, fr.y());
1501
if (index == count() - 1) {
1502
p->drawLine(fr.x(), fr.y() + fr.height() - 1,
1503
fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1504
p->setPen(palette().color(QPalette::Mid));
1505
p->drawLine(fr.x(), fr.y() + fr.height() - 2,
1506
fr.x() + fr.width() - 1, fr.y() + fr.height() - 2);
1512
paintSectionLabel(p, index, fr);
1516
Paints the label of the section at position \a index, inside
1517
rectangle \a fr (which uses widget coordinates) using painter \a
1520
Called by paintSection()
1522
void Q3Header::paintSectionLabel(QPainter *p, int index, const QRect& fr)
1524
int section = mapToSection(index);
1529
QStyleOptionHeader opt = getStyleOption(this, section);
1530
if (d->sortSection == section)
1531
opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
1532
if (index == handleIdx && (state == Pressed || state == Moving)) {
1533
dx = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, this);
1534
dy = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, this);
1535
opt.state |= QStyle::State_Sunken;
1538
opt.state |= QStyle::State_Enabled;
1541
opt.rect.setRect(fr.x() + style()->pixelMetric(QStyle::PM_HeaderMargin) + dx, fr.y() + 2 + dy,
1542
fr.width() - 6, fr.height() - 4);
1544
style()->drawControl(QStyle::CE_HeaderLabel, &opt, p, this);
1546
int arrowWidth = (orient == Qt::Horizontal ? height() : width()) / 2;
1547
int arrowHeight = fr.height() - 6;
1548
QSize ssh = sectionSizeHint(section, p->fontMetrics());
1549
int tw = (orient == Qt::Horizontal ? ssh.width() : ssh.height());
1552
if (style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this) & Qt::AlignRight)
1553
ew = fr.width() - tw - 8;
1554
if (d->sortSection == section && tw <= fr.width()) {
1556
tw = fr.width() - tw;
1557
ew = fr.width() - ew - tw;
1559
opt.state = QStyle::State_None;
1561
opt.state |= QStyle::State_Enabled;
1562
if (d->sortDirection)
1563
opt.state |= QStyle::State_DownArrow;
1565
opt.state |= QStyle::State_UpArrow;
1566
QRect ar(fr.x() + tw - arrowWidth - 6 + ew, 4, arrowWidth, arrowHeight);
1567
if (label(section).isRightToLeft())
1568
ar.moveBy( 2*(fr.right() - ar.right()) + ar.width() - fr.width(), 0 );
1570
style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &opt, p, this);
1576
void Q3Header::paintEvent(QPaintEvent *e)
1579
p.setPen(palette().buttonText().color());
1580
int pos = orient == Qt::Horizontal ? e->rect().left() : e->rect().top();
1581
int id = mapToIndex(sectionAt(pos + offset()));
1591
for (int i = id; i >= 0; i--) {
1593
paintSection(&p, i, r);
1594
if (r.right() >= e->rect().right())
1599
for (int i = id; i <= count(); i++) {
1602
If the last section is clickable (and thus is
1603
painted raised), draw the virtual section count()
1604
as well. Otherwise it looks ugly.
1606
if (i < count() || d->clicks[mapToSection(count() - 1)])
1607
paintSection(&p, i, r);
1608
if (hasFocus() && d->focusIdx == i) {
1609
QStyleOptionFocusRect opt;
1610
opt.rect.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4);
1611
opt.palette = palette();
1612
opt.state = QStyle::State_None;
1613
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
1615
if (orient == Qt::Horizontal && r. right() >= e->rect().right() ||
1616
orient == Qt::Vertical && r. bottom() >= e->rect().bottom())
1626
Sets the sort indicator to \a ascending. Use the other overload instead.
1629
void Q3Header::setSortIndicator(int section, bool ascending)
1631
d->sortSection = section;
1633
oldHandleIdx = section;
1634
d->sortDirection = ascending;
1640
\fn void Q3Header::setSortIndicator(int section, Qt::SortOrder order)
1642
Sets a sort indicator onto the specified \a section. The indicator's
1643
\a order is either Ascending or Descending.
1645
Only one section can show a sort indicator at any one time. If you
1646
don't want any section to show a sort indicator pass a \a section
1649
\sa sortIndicatorSection(), sortIndicatorOrder()
1653
Returns the section showing the sort indicator or -1 if there is no sort indicator.
1655
\sa setSortIndicator(), sortIndicatorOrder()
1658
int Q3Header::sortIndicatorSection() const
1660
return d->sortSection;
1664
Returns the implied sort order of the Q3Headers sort indicator.
1666
\sa setSortIndicator(), sortIndicatorSection()
1669
Qt::SortOrder Q3Header::sortIndicatorOrder() const
1671
return d->sortDirection ? Qt::AscendingOrder : Qt::DescendingOrder;
1675
Resizes section \a section to \a s pixels wide (or high).
1678
void Q3Header::resizeSection(int section, int s)
1680
setCellSize(section, s);
1685
Returns the width (or height) of the \a section in pixels.
1688
int Q3Header::sectionSize(int section) const
1690
if (section < 0 || section >= count())
1692
return d->sizes[section];
1696
Returns the position (in pixels) at which the \a section starts.
1701
int Q3Header::sectionPos(int section) const
1703
if (d->positionsDirty)
1704
((Q3Header *)this)->calculatePositions();
1705
if (section < 0 || section >= count() )
1707
return d->positions[d->s2i[section]];
1711
Returns the index of the section which contains the position \a
1712
pos given in pixels from the left (or top).
1717
int Q3Header::sectionAt(int pos) const
1720
pos = d->lastPos - pos;
1721
return d->sectionAt(pos);
1725
Returns the number of the section that is displayed at index
1729
int Q3Header::mapToSection(int index) const
1731
return (index >= 0 && index < count()) ? d->i2s[index] : -1;
1735
Returns the index position at which section \a section is
1739
int Q3Header::mapToIndex(int section) const
1741
return (section >= 0 && section < count()) ? d->s2i[section] : -1;
1745
Moves section \a section to index position \a toIndex.
1748
void Q3Header::moveSection(int section, int toIndex)
1750
int fromIndex = mapToIndex(section);
1751
if (fromIndex == toIndex ||
1752
fromIndex < 0 || fromIndex > count() ||
1753
toIndex < 0 || toIndex > count())
1756
int idx = d->i2s[fromIndex];
1757
if (fromIndex < toIndex) {
1758
for (i = fromIndex; i < toIndex - 1; i++) {
1760
d->i2s[i] = t = d->i2s[i+1];
1763
d->i2s[toIndex-1] = idx;
1764
d->s2i[idx] = toIndex-1;
1766
for (i = fromIndex; i > toIndex; i--) {
1768
d->i2s[i] = t = d->i2s[i-1];
1771
d->i2s[toIndex] = idx;
1772
d->s2i[idx] = toIndex;
1774
calculatePositions();
1778
Returns true if section \a section is clickable; otherwise returns
1781
If \a section is out of range (negative or larger than count() -
1782
1): returns true if all sections are clickable; otherwise returns
1785
\sa setClickEnabled()
1788
bool Q3Header::isClickEnabled(int section) const
1790
if (section >= 0 && section < count()) {
1791
return (bool)d->clicks[section];
1794
for (int i = 0; i < count(); ++i) {
1802
Returns true if section \a section is resizeable; otherwise
1805
If \a section is -1 then this function applies to all sections,
1806
i.e. returns true if all sections are resizeable; otherwise
1809
\sa setResizeEnabled()
1812
bool Q3Header::isResizeEnabled(int section) const
1814
if (section >= 0 && section < count()) {
1815
return (bool)d->resize[section];
1818
for (int i = 0; i < count();++i) {
1825
bool Q3Header::isMovingEnabled() const
1832
void Q3Header::setUpdatesEnabled(bool enable)
1835
calculatePositions();
1836
QWidget::setUpdatesEnabled(enable);
1840
bool Q3Header::reverse () const
1843
return (orient == Qt::Horizontal && QApplication::reverseLayout());
1850
void Q3Header::resizeEvent(QResizeEvent *e)
1853
QWidget::resizeEvent(e);
1855
if(d->lastPos < width()) {
1860
adjustHeaderSize(orientation() == Qt::Horizontal ?
1861
width() - e->oldSize().width() : height() - e->oldSize().height());
1862
if ((orientation() == Qt::Horizontal && height() != e->oldSize().height())
1863
|| (orientation() == Qt::Vertical && width() != e->oldSize().width()))
1870
\fn void Q3Header::adjustHeaderSize()
1872
Adjusts the size of the sections to fit the size of the header as
1873
completely as possible. Only sections for which isStretchEnabled()
1874
is true will be resized.
1877
void Q3Header::adjustHeaderSize(int diff)
1882
// we skip the adjustHeaderSize when trying to resize the last column which is set to stretchable
1883
if (d->fullSize == (count() -1) &&
1884
(d->lastPos - d->sizes[count() -1]) > (orient == Qt::Horizontal ? width() : height()))
1887
if (d->fullSize >= 0) {
1888
int sec = mapToSection(d->fullSize);
1889
int lsec = mapToSection(count() - 1);
1890
int ns = sectionSize(sec) +
1891
(orientation() == Qt::Horizontal ?
1892
width() : height()) - (sectionPos(lsec) + sectionSize(lsec));
1893
int os = sectionSize(sec);
1896
setCellSize(sec, ns);
1898
emit sizeChange(sec, os, ns);
1899
} else if (d->fullSize == -1) {
1900
int df = diff / count();
1901
int part = orientation() == Qt::Horizontal ? width() / count() : height() / count();
1902
for (int i = 0; i < count() - 1; ++i) {
1903
int sec = mapToIndex(i);
1904
int os = sectionSize(sec);
1905
int ns = diff != -1 ? os + df : part;
1908
setCellSize(sec, ns);
1909
emit sizeChange(sec, os, ns);
1911
int sec = mapToIndex(count() - 1);
1912
int ns = (orientation() == Qt::Horizontal ? width() : height()) - sectionPos(sec);
1913
int os = sectionSize(sec);
1916
setCellSize(sec, ns);
1918
emit sizeChange(sec, os, ns);
1923
Returns the total width of all the header columns.
1925
int Q3Header::headerWidth() const
1928
((Q3Header*)this)->calculatePositions();
1929
d->pos_dirty = false;
1934
void Q3Header::calculatePositions(bool onlyVisible, int start)
1936
d->positionsDirty = false;
1937
d->lastPos = count() > 0 ? d->positions[start] : 0;
1938
for (int i = start; i < count(); i++) {
1939
d->positions[i] = d->lastPos;
1940
d->lastPos += d->sizes[d->i2s[i]];
1941
if (onlyVisible && d->lastPos > offset() +
1942
(orientation() == Qt::Horizontal ? width() : height()))
1945
d->pos_dirty = onlyVisible;
1950
\property Q3Header::stretching
1951
\brief whether the header sections always take up the full width
1952
(or height) of the header
1957
If \a b is true, section \a section will be resized when the
1958
header is resized, so that the sections take up the full width (or
1959
height for vertical headers) of the header; otherwise section \a
1960
section will be set to be unstretchable and will not resize when
1961
the header is resized.
1963
If \a section is -1, and if \a b is true, then all sections will
1964
be resized equally when the header is resized so that they take up
1965
the full width (or height for vertical headers) of the header;
1966
otherwise all the sections will be set to be unstretchable and
1967
will not resize when the header is resized.
1969
\sa adjustHeaderSize()
1972
void Q3Header::setStretchEnabled(bool b, int section)
1975
d->fullSize = section;
1981
bool Q3Header::isStretchEnabled() const
1983
return d->fullSize == -1;
1989
Returns true if section \a section will resize to take up the full
1990
width (or height) of the header; otherwise returns false. If at
1991
least one section has stretch enabled the sections will always
1992
take up the full width of the header.
1994
\sa setStretchEnabled()
1997
bool Q3Header::isStretchEnabled(int section) const
1999
return d->fullSize == section;
2005
void Q3Header::changeEvent(QEvent *ev)
2007
if(ev->type() == QEvent::FontChange) {
2008
QFontMetrics fm = fontMetrics();
2009
d->height = (orient == Qt::Horizontal) ? fm.lineSpacing() + 6 : fm.width(' ');
2011
QWidget::changeEvent(ev);
2014
#endif // QT_NO_HEADER