source: trunk/src/qt3support/widgets/q3header.cpp@ 1023

Last change on this file since 1023 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: 55.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt3Support 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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "q3header.h"
43#ifndef QT_NO_HEADER
44#include "qapplication.h"
45#include "qbitarray.h"
46#include "qcursor.h"
47#include "qdrawutil.h"
48#include "qevent.h"
49#include "qpainter.h"
50#include "qpixmap.h"
51#include "qstyle.h"
52#include "qstyleoption.h"
53#include "qvector.h"
54
55QT_BEGIN_NAMESPACE
56
57class Q3HeaderData
58{
59public:
60 Q3HeaderData(int n)
61 {
62 count = n;
63 sizes.resize(n);
64 positions.resize(n);
65 labels.resize(n);
66 nullStringLabels.resize(n);
67 icons.resize(n);
68 i2s.resize(n);
69 s2i.resize(n);
70 clicks.resize(n);
71 resize.resize(n);
72 int p =0;
73 for (int i = 0; i < n; i ++) {
74 sizes[i] = 88;
75 i2s[i] = i;
76 s2i[i] = i;
77 positions[i] = p;
78 p += sizes[i];
79 }
80 clicks_default = true;
81 resize_default = true;
82 clicks.fill(clicks_default);
83 resize.fill(resize_default);
84 move = true;
85 sortSection = -1;
86 sortDirection = true;
87 positionsDirty = true;
88 lastPos = 0;
89 fullSize = -2;
90 pos_dirty = false;
91 is_a_table_header = false;
92 focusIdx = 0;
93 }
94 ~Q3HeaderData()
95 {
96 for (int i = 0; i < icons.size(); ++i)
97 delete icons.at(i);
98 }
99
100
101 QVector<int> sizes;
102 int height; // we abuse the heights as widths for vertical layout
103 bool heightDirty;
104 QVector<int> positions; // sorted by index
105 QVector<QString> labels;
106 QVector<QIcon *> icons;
107 QVector<int> i2s;
108 QVector<int> s2i;
109
110 QBitArray clicks;
111 QBitArray resize;
112 QBitArray nullStringLabels;
113 uint move : 1;
114 uint clicks_default : 1; // default value for new clicks bits
115 uint resize_default : 1; // default value for new resize bits
116 uint pos_dirty : 1;
117 uint is_a_table_header : 1;
118 bool sortDirection;
119 bool positionsDirty;
120 int sortSection;
121 int count;
122 int lastPos;
123 int fullSize;
124 int focusIdx;
125 int pressDelta;
126
127 int sectionAt(int pos) {
128 // positions is sorted by index, not by section
129 if (!count)
130 return -1;
131 int l = 0;
132 int r = count - 1;
133 int i = ((l+r+1) / 2);
134 while (r - l) {
135 if (positions[i] > pos)
136 r = i -1;
137 else
138 l = i;
139 i = ((l+r+1) / 2);
140 }
141 if (positions[i] <= pos && pos <= positions[i] + sizes[i2s[i]])
142 return i2s[i];
143 return -1;
144 }
145};
146
147static QStyleOptionHeader getStyleOption(const Q3Header *header, int section)
148{
149 QStyleOptionHeader opt;
150 opt.init(header);
151 opt.section = section;
152 opt.textAlignment = Qt::AlignVCenter;
153 opt.iconAlignment = Qt::AlignVCenter;
154 if (header->iconSet(section))
155 opt.icon = *header->iconSet(section);
156 opt.text = header->label(section);
157 if (header->orientation() == Qt::Horizontal)
158 opt.state = QStyle::State_Horizontal;
159 return opt;
160}
161
162bool qt_get_null_label_bit(Q3HeaderData *data, int section)
163{
164 return data->nullStringLabels.testBit(section);
165}
166
167void qt_set_null_label_bit(Q3HeaderData *data, int section, bool b)
168{
169 data->nullStringLabels.setBit(section, b);
170}
171
172/*!
173 \class Q3Header
174 \brief The Q3Header class provides a header row or column, e.g. for
175 tables and listviews.
176
177 \compat
178
179 This class provides a header, e.g. a vertical header to display
180 row labels, or a horizontal header to display column labels. It is
181 used by Q3Table and Q3ListView for example.
182
183 A header is composed of one or more \e sections, each of which can
184 display a text label and an \link QIcon icon\endlink. A sort
185 indicator (an arrow) can also be displayed using
186 setSortIndicator().
187
188 Sections are added with addLabel() and removed with removeLabel().
189 The label and icon are set in addLabel() and can be changed
190 later with setLabel(). Use count() to retrieve the number of
191 sections in the header.
192
193 The orientation of the header is set with setOrientation(). If
194 setStretchEnabled() is true, the sections will expand to take up
195 the full width (height for vertical headers) of the header. The
196 user can resize the sections manually if setResizeEnabled() is
197 true. Call adjustHeaderSize() to have the sections resize to
198 occupy the full width (or height).
199
200 A section can be moved with moveSection(). If setMovingEnabled()
201 is true (the default)the user may drag a section from one position
202 to another. If a section is moved, the index positions at which
203 sections were added (with addLabel()), may not be the same after the
204 move. You don't have to worry about this in practice because the
205 Q3Header API works in terms of section numbers, so it doesn't matter
206 where a particular section has been moved to.
207
208 If you want the current index position of a section call
209 mapToIndex() giving it the section number. (This is the number
210 returned by the addLabel() call which created the section.) If you
211 want to get the section number of a section at a particular index
212 position call mapToSection() giving it the index number.
213
214 Here's an example to clarify mapToSection() and mapToIndex():
215
216 \table
217 \header \i41 Index positions
218 \row \i 0 \i 1 \i 2 \i 3
219 \header \i41 Original section ordering
220 \row \i Sect 0 \i Sect 1 \i Sect 2 \i Sect 3
221 \header \i41 Ordering after the user moves a section
222 \row \i Sect 0 \i Sect 2 \i Sect 3 \i Sect 1
223 \endtable
224
225 \table
226 \header \i \e k \i mapToSection(\e k) \i mapToIndex(\e k)
227 \row \i 0 \i 0 \i 0
228 \row \i 1 \i 2 \i 3
229 \row \i 2 \i 3 \i 1
230 \row \i 3 \i 1 \i 2
231 \endtable
232
233 In the example above, if we wanted to find out which section is at
234 index position 3 we'd call mapToSection(3) and get a section
235 number of 1 since section 1 was moved. Similarly, if we wanted to
236 know which index position section 2 occupied we'd call
237 mapToIndex(2) and get an index of 1.
238
239 Q3Header provides the clicked(), pressed() and released() signals.
240 If the user changes the size of a section, the sizeChange() signal
241 is emitted. If you want to have a sizeChange() signal emitted
242 continuously whilst the user is resizing (rather than just after
243 the resizing is finished), use setTracking(). If the user moves a
244 section the indexChange() signal is emitted.
245
246 \sa Q3ListView Q3Table
247*/
248
249
250
251/*!
252 Constructs a horizontal header called \a name, with parent \a
253 parent.
254*/
255
256Q3Header::Q3Header(QWidget *parent, const char *name)
257 : QWidget(parent, name, Qt::WStaticContents)
258{
259 orient = Qt::Horizontal;
260 init(0);
261}
262
263/*!
264 Constructs a horizontal header called \a name, with \a n sections
265 and parent \a parent.
266*/
267
268Q3Header::Q3Header(int n, QWidget *parent, const char *name)
269 : QWidget(parent, name, Qt::WStaticContents)
270{
271 orient = Qt::Horizontal;
272 init(n);
273}
274
275/*!
276 Destroys the header and all its sections.
277*/
278
279Q3Header::~Q3Header()
280{
281 delete d;
282 d = 0;
283}
284
285/*! \reimp
286 */
287
288void Q3Header::showEvent(QShowEvent *e)
289{
290 calculatePositions();
291 QWidget::showEvent(e);
292}
293
294/*!
295 \fn void Q3Header::sizeChange(int section, int oldSize, int newSize)
296
297 This signal is emitted when the user has changed the size of a \a
298 section from \a oldSize to \a newSize. This signal is typically
299 connected to a slot that repaints the table or list that contains
300 the header.
301*/
302
303/*!
304 \fn void Q3Header::clicked(int section)
305
306 If isClickEnabled() is true, this signal is emitted when the user
307 clicks section \a section.
308
309 \sa pressed(), released()
310*/
311
312/*!
313 \fn void Q3Header::pressed(int section)
314
315 This signal is emitted when the user presses section \a section
316 down.
317
318 \sa released()
319*/
320
321/*!
322 \fn void Q3Header::released(int section)
323
324 This signal is emitted when section \a section is released.
325
326 \sa pressed()
327*/
328
329
330/*!
331 \fn void Q3Header::indexChange(int section, int fromIndex, int toIndex)
332
333 This signal is emitted when the user moves section \a section from
334 index position \a fromIndex, to index position \a toIndex.
335*/
336
337/*!
338 \fn void Q3Header::moved(int fromIndex, int toIndex)
339
340 Use indexChange() instead.
341
342 This signal is emitted when the user has moved the section which
343 is displayed at the index \a fromIndex to the index \a toIndex.
344*/
345
346/*!
347 \fn void Q3Header::sectionClicked(int index)
348
349 Use clicked() instead.
350
351 This signal is emitted when a part of the header is clicked. \a
352 index is the index at which the section is displayed.
353
354 In a list view this signal would typically be connected to a slot
355 that sorts the specified column (or row).
356*/
357
358/*! \fn int Q3Header::cellSize(int) const
359
360 Use sectionSize() instead.
361
362 Returns the size in pixels of the section that is displayed at
363 the index \a i.
364*/
365
366/*!
367 \fn void Q3Header::sectionHandleDoubleClicked(int section)
368
369 This signal is emitted when the user doubleclicks on the edge
370 (handle) of section \a section.
371*/
372
373/*!
374
375 Use sectionPos() instead.
376
377 Returns the position in pixels of the section that is displayed at the
378 index \a i. The position is measured from the start of the header.
379*/
380
381int Q3Header::cellPos(int i) const
382{
383 if (i == count() && i > 0)
384 return d->positions[i-1] + d->sizes[d->i2s[i-1]]; // compatibility
385 return sectionPos(mapToSection(i));
386}
387
388
389/*!
390 \property Q3Header::count
391 \brief the number of sections in the header
392*/
393
394int Q3Header::count() const
395{
396 return d->count;
397}
398
399
400/*!
401 \property Q3Header::tracking
402 \brief whether the sizeChange() signal is emitted continuously
403
404 If tracking is on, the sizeChange() signal is emitted continuously
405 while the mouse is moved (i.e. when the header is resized),
406 otherwise it is only emitted when the mouse button is released at
407 the end of resizing.
408
409 Tracking defaults to false.
410*/
411
412
413/*
414 Initializes with \a n columns.
415*/
416void Q3Header::init(int n)
417{
418 state = Idle;
419 cachedPos = 0; // unused
420 d = new Q3HeaderData(n);
421 d->height = 0;
422 d->heightDirty = true;
423 offs = 0;
424 if(reverse())
425 offs = d->lastPos - width();
426 oldHandleIdx = oldHIdxSize = handleIdx = 0;
427
428 setMouseTracking(true);
429 trackingIsOn = false;
430 setBackgroundRole(QPalette::Button);
431 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
432 setAttribute(Qt::WA_PaintOutsidePaintEvent);
433}
434
435/*!
436 \property Q3Header::orientation
437 \brief the header's orientation
438
439 The orientation is either Qt::Vertical or Qt::Horizontal (the
440 default).
441
442 Call setOrientation() before adding labels if you don't provide a
443 size parameter otherwise the sizes will be incorrect.
444*/
445
446void Q3Header::setOrientation(Qt::Orientation orientation)
447{
448 if (orient == orientation)
449 return;
450 orient = orientation;
451 if (orient == Qt::Horizontal)
452 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
453 else
454 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
455 update();
456 updateGeometry();
457}
458
459
460/*
461 Paints a rectangle starting at \a p, with length \s.
462*/
463void Q3Header::paintRect(int p, int s)
464{
465 QPainter paint(this);
466 paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
467 if (reverse())
468 paint.drawRect(p - s, 3, s, height() - 5);
469 else if (orient == Qt::Horizontal)
470 paint.drawRect(p, 3, s, height() - 5);
471 else
472 paint.drawRect(3, p, height() - 5, s);
473}
474
475/*
476 Marks the division line at \a idx.
477*/
478void Q3Header::markLine(int idx)
479{
480 QPainter paint(this);
481 paint.setPen(QPen(Qt::black, 1, Qt::DotLine));
482 int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
483 int p = pPos(idx);
484 int x = p - MARKSIZE/2;
485 int y = 2;
486 int x2 = p + MARKSIZE/2;
487 int y2 = height() - 3;
488 if (orient == Qt::Vertical) {
489 int t = x; x = y; y = t;
490 t = x2; x2 = y2; y2 = t;
491 }
492
493 paint.drawLine(x, y, x2, y);
494 paint.drawLine(x, y+1, x2, y+1);
495
496 paint.drawLine(x, y2, x2, y2);
497 paint.drawLine(x, y2-1, x2, y2-1);
498
499 paint.drawLine(x, y, x, y2);
500 paint.drawLine(x+1, y, x+1, y2);
501
502 paint.drawLine(x2, y, x2, y2);
503 paint.drawLine(x2-1, y, x2-1, y2);
504}
505
506/*
507 Removes the mark at the division line at \a idx.
508*/
509void Q3Header::unMarkLine(int idx)
510{
511 if (idx < 0)
512 return;
513 int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
514 int p = pPos(idx);
515 int x = p - MARKSIZE/2;
516 int y = 2;
517 int x2 = p + MARKSIZE/2;
518 int y2 = height() - 3;
519 if (orient == Qt::Vertical) {
520 int t = x; x = y; y = t;
521 t = x2; x2 = y2; y2 = t;
522 }
523 repaint(x, y, x2-x+1, y2-y+1);
524}
525
526/*! \fn int Q3Header::cellAt(int) const
527
528 Use sectionAt() instead.
529
530 Returns the index at which the section is displayed, which contains
531 \a pos in widget coordinates, or -1 if \a pos is outside the header
532 sections.
533*/
534
535/*
536 Tries to find a line that is not a neighbor of \c handleIdx.
537*/
538int Q3Header::findLine(int c)
539{
540 int i = 0;
541 if (c > d->lastPos || (reverse() && c < 0)) {
542 return d->count;
543 } else {
544 int section = sectionAt(c);
545 if (section < 0)
546 return handleIdx;
547 i = d->s2i[section];
548 }
549 int MARKSIZE = style()->pixelMetric(QStyle::PM_HeaderMarkSize);
550 if (i == handleIdx)
551 return i;
552 if (i == handleIdx - 1 && pPos(handleIdx) - c > MARKSIZE/2)
553 return i;
554 if (i == handleIdx + 1 && c - pPos(i) > MARKSIZE/2)
555 return i + 1;
556 if (c - pPos(i) > pSize(i) / 2)
557 return i + 1;
558 else
559 return i;
560}
561
562/*!
563 Returns the handle at position \a p, or -1 if there is no handle at \a p.
564*/
565int Q3Header::handleAt(int p)
566{
567 int section = d->sectionAt(p);
568 if (section >= 0) {
569 int GripMargin = (bool)d->resize[section] ?
570 style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
571 int index = d->s2i[section];
572 if ((index > 0 && p < d->positions[index] + GripMargin) ||
573 (p > d->positions[index] + d->sizes[section] - GripMargin)) {
574 if (index > 0 && p < d->positions[index] + GripMargin)
575 section = d->i2s[--index];
576 // don't show icon if streaching is enabled it is at the end of the last section
577 if (d->resize.testBit(section) && (d->fullSize == -2 || index != count() - 1)) {
578 return section;
579 }
580 }
581 }
582
583 return -1;
584}
585
586/*!
587 Use moveSection() instead.
588
589 Moves the section that is currently displayed at index \a fromIdx
590 to index \a toIdx.
591*/
592
593void Q3Header::moveCell(int fromIdx, int toIdx)
594{
595 moveSection(mapToSection(fromIdx), toIdx);
596}
597
598
599
600/*!
601 Move and signal and repaint.
602 */
603
604void Q3Header::handleColumnMove(int fromIdx, int toIdx)
605{
606 int s = d->i2s[fromIdx];
607 if (fromIdx < toIdx)
608 toIdx++; //Convert to
609 QRect r = sRect(fromIdx);
610 r |= sRect(toIdx);
611 moveSection(s, toIdx);
612 update(r);
613 emit moved(fromIdx, toIdx);
614 emit indexChange(s, fromIdx, toIdx);
615}
616
617/*!
618 \reimp
619*/
620void Q3Header::keyPressEvent(QKeyEvent *e)
621{
622 int i = d->focusIdx;
623 if (e->key() == Qt::Key_Space) {
624 //don't do it if we're doing something with the mouse
625 if (state == Idle && d->clicks[d->i2s[d->focusIdx] ]) {
626 handleIdx = i;
627 state = Pressed;
628 repaint(sRect(handleIdx));
629 emit pressed(d->i2s[i]);
630 }
631 } else if ((orientation() == Qt::Horizontal && (e->key() == Qt::Key_Right || e->key() == Qt::Key_Left))
632 || (orientation() == Qt::Vertical && (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down))) {
633 int dir = e->key() == Qt::Key_Right || e->key() == Qt::Key_Down ? 1 : -1;
634 int s = d->i2s[i];
635 if (e->state() & Qt::ControlButton && d->resize[s]) {
636 //resize
637 int step = e->state() & Qt::ShiftButton ? dir : 10*dir;
638 int c = d->positions[i] + d->sizes[s] + step;
639 handleColumnResize(i, c, true);
640 } else if (e->state() & (Qt::AltButton|Qt::MetaButton) && d->move) {
641 //move section
642 int i2 = (i + count() + dir) % count();
643 d->focusIdx = i2;
644 handleColumnMove(i, i2);
645 } else {
646 //focus on different section
647 QRect r = sRect(d->focusIdx);
648 d->focusIdx = (d->focusIdx + count() + dir) % count();
649 r |= sRect(d->focusIdx);
650 update(r);
651 }
652 } else {
653 e->ignore();
654 }
655}
656
657/*!
658 \reimp
659*/
660void Q3Header::keyReleaseEvent(QKeyEvent *e)
661{
662 switch (e->key()) {
663 case Qt::Key_Space:
664 //double check that this wasn't started with the mouse
665 if (state == Pressed && handleIdx == d->focusIdx) {
666 repaint(sRect(handleIdx));
667 int section = d->i2s[d->focusIdx];
668 emit released(section);
669 emit sectionClicked(handleIdx);
670 emit clicked(section);
671 state = Idle;
672 handleIdx = -1;
673 }
674 break;
675 default:
676 e->ignore();
677 }
678}
679
680
681/*!
682 \reimp
683*/
684void Q3Header::mousePressEvent(QMouseEvent *e)
685{
686 if (e->button() != Qt::LeftButton || state != Idle)
687 return;
688 oldHIdxSize = handleIdx;
689 handleIdx = 0;
690 int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
691 c += offset();
692 if (reverse())
693 c = d->lastPos - c;
694
695 int section = d->sectionAt(c);
696 if (section < 0)
697 return;
698 int GripMargin = (bool)d->resize[section] ?
699 style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
700 int index = d->s2i[section];
701
702 if ((index > 0 && c < d->positions[index] + GripMargin) ||
703 (c > d->positions[index] + d->sizes[section] - GripMargin)) {
704 if (c < d->positions[index] + GripMargin)
705 handleIdx = index-1;
706 else
707 handleIdx = index;
708 if (d->lastPos <= (orient == Qt::Horizontal ? width() :
709 height()) && d->fullSize != -2 && handleIdx == count() - 1) {
710 handleIdx = -1;
711 return;
712 }
713 oldHIdxSize = d->sizes[d->i2s[handleIdx]];
714 state = d->resize[d->i2s[handleIdx] ] ? Sliding : Blocked;
715 } else if (index >= 0) {
716 oldHandleIdx = handleIdx = index;
717 moveToIdx = -1;
718 state = d->clicks[d->i2s[handleIdx] ] ? Pressed : Blocked;
719 clickPos = c;
720 repaint(sRect(handleIdx));
721 if(oldHandleIdx != handleIdx)
722 repaint(sRect(oldHandleIdx));
723 emit pressed(section);
724 }
725
726 d->pressDelta = c - (d->positions[handleIdx] + d->sizes[d->i2s[handleIdx]]);
727}
728
729/*!
730 \reimp
731*/
732void Q3Header::mouseReleaseEvent(QMouseEvent *e)
733{
734 if (e->button() != Qt::LeftButton)
735 return;
736 int oldOldHandleIdx = oldHandleIdx;
737 State oldState = state;
738 state = Idle;
739 switch (oldState) {
740 case Pressed: {
741 int section = d->i2s[handleIdx];
742 emit released(section);
743 if (sRect(handleIdx).contains(e->pos())) {
744 oldHandleIdx = handleIdx;
745 emit sectionClicked(handleIdx);
746 emit clicked(section);
747 } else {
748 handleIdx = oldHandleIdx;
749 }
750 repaint(sRect(handleIdx));
751 if (oldOldHandleIdx != handleIdx)
752 repaint(sRect(oldOldHandleIdx));
753 } break;
754 case Sliding: {
755 int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
756 c += offset();
757 if (reverse())
758 c = d->lastPos - c;
759 handleColumnResize(handleIdx, c - d->pressDelta, true);
760 } break;
761 case Moving: {
762#ifndef QT_NO_CURSOR
763 unsetCursor();
764#endif
765 int section = d->i2s[handleIdx];
766 if (handleIdx != moveToIdx && moveToIdx != -1) {
767 moveSection(section, moveToIdx);
768 handleIdx = oldHandleIdx;
769 emit moved(handleIdx, moveToIdx);
770 emit indexChange(section, handleIdx, moveToIdx);
771 emit released(section);
772 repaint(); // a bit overkill, but removes the handle as well
773 } else {
774 if (sRect(handleIdx).contains(e->pos())) {
775 oldHandleIdx = handleIdx;
776 emit released(section);
777 emit sectionClicked(handleIdx);
778 emit clicked(section);
779 } else {
780 handleIdx = oldHandleIdx;
781 }
782 repaint(sRect(handleIdx));
783 if(oldOldHandleIdx != handleIdx)
784 repaint(sRect(oldOldHandleIdx));
785 }
786 break;
787 }
788 case Blocked:
789 //nothing
790 break;
791 default:
792 // empty, probably. Idle, at any rate.
793 break;
794 }
795}
796
797/*!
798 \reimp
799*/
800void Q3Header::mouseMoveEvent(QMouseEvent *e)
801{
802 int c = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
803 c += offset();
804
805 int pos = c;
806 if(reverse())
807 c = d->lastPos - c;
808
809 switch(state) {
810 case Idle:
811#ifndef QT_NO_CURSOR
812 if (handleAt(c) < 0)
813 unsetCursor();
814 else if (orient == Qt::Horizontal)
815 setCursor(Qt::splitHCursor);
816 else
817 setCursor(Qt::splitVCursor);
818#endif
819 break;
820 case Blocked:
821 break;
822 case Pressed:
823 if (QABS(c - clickPos) > 4 && d->move) {
824 state = Moving;
825 moveToIdx = -1;
826#ifndef QT_NO_CURSOR
827 if (orient == Qt::Horizontal)
828 setCursor(Qt::SizeHorCursor);
829 else
830 setCursor(Qt::SizeVerCursor);
831#endif
832 }
833 break;
834 case Sliding:
835 handleColumnResize(handleIdx, c, false, false);
836 break;
837 case Moving: {
838 int newPos = findLine(pos);
839 if (newPos != moveToIdx) {
840 if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
841 repaint(sRect(handleIdx));
842 else
843 unMarkLine(moveToIdx);
844 moveToIdx = newPos;
845 if (moveToIdx == handleIdx || moveToIdx == handleIdx + 1)
846 paintRect(pPos(handleIdx), pSize(handleIdx));
847 else
848 markLine(moveToIdx);
849 }
850 break;
851 }
852 default:
853 qWarning("Q3Header::mouseMoveEvent: (%s) unknown state", objectName().toLocal8Bit().data());
854 break;
855 }
856}
857
858/*! \reimp */
859
860void Q3Header::mouseDoubleClickEvent(QMouseEvent *e)
861{
862 int p = orient == Qt::Horizontal ? e->pos().x() : e->pos().y();
863 p += offset();
864 if(reverse())
865 p = d->lastPos - p;
866
867 int header = handleAt(p);
868 if (header >= 0)
869 emit sectionHandleDoubleClicked(header);
870}
871
872/*
873 Handles resizing of sections. This means it redraws the relevant parts
874 of the header.
875*/
876
877void Q3Header::handleColumnResize(int index, int c, bool final, bool recalcAll)
878{
879 int section = d->i2s[index];
880 int GripMargin = (bool)d->resize[section] ?
881 style()->pixelMetric(QStyle::PM_HeaderGripMargin) : 0;
882 int lim = d->positions[index] + 2*GripMargin;
883 if (c == lim)
884 return;
885 if (c < lim)
886 c = lim;
887 int oldSize = d->sizes[section];
888 int newSize = c - d->positions[index];
889 d->sizes[section] = newSize;
890
891 calculatePositions(!recalcAll, !recalcAll ? section : 0);
892
893 int pos = d->positions[index]-offset();
894 if(reverse()) // repaint the whole thing. Could be optimized (lars)
895 repaint(0, 0, width(), height());
896 else if (orient == Qt::Horizontal)
897 repaint(pos, 0, width() - pos, height());
898 else
899 repaint(0, pos, width(), height() - pos);
900
901 int os = 0, ns = 0;
902 if (tracking() && oldSize != newSize) {
903 os = oldSize;
904 ns = newSize;
905 emit sizeChange(section, oldSize, newSize);
906 } else if (!tracking() && final && oldHIdxSize != newSize) {
907 os = oldHIdxSize;
908 ns = newSize;
909 emit sizeChange(section, oldHIdxSize, newSize);
910 }
911
912 if (os != ns) {
913 if (d->fullSize == -1) {
914 d->fullSize = count() - 1;
915 adjustHeaderSize();
916 d->fullSize = -1;
917 } else if (d->fullSize >= 0) {
918 int old = d->fullSize;
919 d->fullSize = count() - 1;
920 adjustHeaderSize();
921 d->fullSize = old;
922 }
923 }
924}
925
926/*!
927 Returns the rectangle covered by the section at index \a index.
928*/
929
930QRect Q3Header::sRect(int index)
931{
932
933 int section = mapToSection(index);
934 if (count() > 0 && index >= count()) {
935 int s = d->positions[count() - 1] - offset() +
936 d->sizes[mapToSection(count() - 1)];
937 if (orient == Qt::Horizontal)
938 return QRect(s, 0, width() - s + 10, height());
939 else
940 return QRect(0, s, width(), height() - s + 10);
941 }
942 if (section < 0)
943 return rect(); // ### eeeeevil
944
945 if (reverse())
946 return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
947 0, d->sizes[section], height());
948 else if (orient == Qt::Horizontal)
949 return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
950 else
951 return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
952}
953
954/*!
955 Returns the rectangle covered by section \a section.
956*/
957
958QRect Q3Header::sectionRect(int section) const
959{
960 int index = mapToIndex(section);
961 if (section < 0)
962 return rect(); // ### eeeeevil
963
964 if (reverse())
965 return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(),
966 0, d->sizes[section], height());
967 else if (orient == Qt::Horizontal)
968 return QRect( d->positions[index]-offset(), 0, d->sizes[section], height());
969 else
970 return QRect(0, d->positions[index]-offset(), width(), d->sizes[section]);
971}
972
973/*!
974 \overload
975
976 Sets the icon for section \a section to \a icon and the text to
977 \a s. The section's width is set to \a size if \a size \>= 0;
978 otherwise it is left unchanged.
979
980 If the section does not exist, nothing happens.
981*/
982
983void Q3Header::setLabel(int section, const QIcon& icon,
984 const QString &s, int size)
985{
986 if (section < 0 || section >= count())
987 return;
988 delete d->icons[section];
989 d->icons[section] = new QIcon(icon);
990 setLabel(section, s, size);
991}
992
993/*!
994 Sets the text of section \a section to \a s. The section's width
995 is set to \a size if \a size \>= 0; otherwise it is left
996 unchanged. Any icon set that has been set for this section remains
997 unchanged.
998
999 If the section does not exist, nothing happens.
1000*/
1001void Q3Header::setLabel(int section, const QString &s, int size)
1002{
1003 if (section < 0 || section >= count())
1004 return;
1005 d->labels[section] = s;
1006 d->nullStringLabels.setBit(section, s.isNull());
1007
1008 setSectionSizeAndHeight(section, size);
1009
1010 if (updatesEnabled()) {
1011 updateGeometry();
1012 calculatePositions();
1013 update();
1014 }
1015}
1016
1017
1018bool qt_qheader_label_return_null_strings = false;
1019/*!
1020 Returns the text for section \a section. If the section does not
1021 exist, returns an empty string.
1022*/
1023QString Q3Header::label(int section) const
1024{
1025 if (section < 0 || section >= count())
1026 return QString();
1027 QString l = d->labels.value(section);
1028 if (!l.isNull())
1029 return l;
1030 if (d->nullStringLabels.testBit(section) || qt_qheader_label_return_null_strings)
1031 return l;
1032 else
1033 return QString::number(section + 1);
1034}
1035
1036/*!
1037 Returns the icon set for section \a section. If the section does
1038 not exist, 0 is returned.
1039*/
1040
1041QIcon *Q3Header::iconSet(int section) const
1042{
1043 if (section < 0 || section >= count())
1044 return 0;
1045 return d->icons[section];
1046}
1047
1048
1049/*!
1050 \overload
1051
1052 Adds a new section with icon \a icon and label text \a s.
1053 Returns the index position where the section was added (at the
1054 right for horizontal headers, at the bottom for vertical headers).
1055 The section's width is set to \a size, unless size is negative in
1056 which case the size is calculated taking account of the size of
1057 the text.
1058*/
1059int Q3Header::addLabel(const QIcon& icon, const QString &s, int size)
1060{
1061 int n = count() + 1;
1062 d->icons.resize(n + 1);
1063 d->icons.insert(n - 1, new QIcon(icon));
1064 return addLabel(s, size);
1065}
1066
1067/*!
1068 Removes section \a section. If the section does not exist, nothing
1069 happens.
1070*/
1071void Q3Header::removeLabel(int section)
1072{
1073 if (section < 0 || section > count() - 1)
1074 return;
1075
1076 int index = d->s2i[section];
1077 int n = --d->count;
1078 int i;
1079 for (i = section; i < n; ++i) {
1080 d->sizes[i] = d->sizes[i+1];
1081 d->labels[i] = d->labels[i+1];
1082 d->labels[i+1] = QString();
1083 d->nullStringLabels[i] = d->nullStringLabels[i+1];
1084 d->nullStringLabels[i+1] = 0;
1085 d->icons[i] = d->icons[i+1];
1086 d->icons[i+1] = 0;
1087 }
1088
1089 d->sizes.resize(n);
1090 d->positions.resize(n);
1091 d->labels.resize(n);
1092 d->nullStringLabels.resize(n);
1093 d->icons.resize(n);
1094
1095 for (i = section; i < n; ++i)
1096 d->s2i[i] = d->s2i[i+1];
1097 d->s2i.resize(n);
1098
1099 if (updatesEnabled()) {
1100 for (i = 0; i < n; ++i)
1101 if (d->s2i[i] > index)
1102 --d->s2i[i];
1103 }
1104
1105 for (i = index; i < n; ++i)
1106 d->i2s[i] = d->i2s[i+1];
1107 d->i2s.resize(n);
1108
1109 if (updatesEnabled()) {
1110 for (i = 0; i < n; ++i)
1111 if (d->i2s[i] > section)
1112 --d->i2s[i];
1113 }
1114
1115 if (updatesEnabled()) {
1116 updateGeometry();
1117 calculatePositions();
1118 update();
1119 }
1120}
1121
1122QSize Q3Header::sectionSizeHint(int section, const QFontMetrics& fm) const
1123{
1124 int iw = 0;
1125 int ih = 0;
1126 if (d->icons[section] != 0) {
1127 QSize isize = d->icons[section]->pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize),
1128 QIcon::Normal).size();
1129 iw = isize.width() + 2;
1130 ih = isize.height();
1131 }
1132
1133 QRect bound;
1134 QString label = d->labels[section];
1135 if (!label.isNull() || d->nullStringLabels.testBit(section)) {
1136 int lines = label.count(QLatin1Char('\n')) + 1;
1137 int w = 0;
1138 if (lines > 1) {
1139 bound.setHeight(fm.height() + fm.lineSpacing() * (lines - 1));
1140 QStringList list = label.split(QLatin1Char('\n'));
1141 for (int i=0; i < list.count(); ++i) {
1142 int tmpw = fm.width(list.at(i));
1143 w = QMAX(w, tmpw);
1144 }
1145 } else {
1146 bound.setHeight(fm.height());
1147 w = fm.width(label);
1148 }
1149 bound.setWidth(w);
1150 }
1151 int arrowWidth = 0;
1152 if (d->sortSection == section)
1153 arrowWidth = ((orient == Qt::Horizontal ? height() : width()) / 2) + 8;
1154 int height = qMax(bound.height() + 2, ih) + 4;
1155 int width = bound.width() + style()->pixelMetric(QStyle::PM_HeaderMargin) * 4
1156 + iw + arrowWidth;
1157 return QSize(width, height);
1158}
1159
1160/*
1161 Sets d->sizes[\a section] to a bounding rect based on its size
1162 hint and font metrics, but constrained by \a size. It also updates
1163 d->height.
1164*/
1165void Q3Header::setSectionSizeAndHeight(int section, int size)
1166{
1167 QSize sz = sectionSizeHint(section, fontMetrics());
1168
1169 if (size < 0) {
1170 if (d->sizes[section] < 0)
1171 d->sizes[section] = (orient == Qt::Horizontal) ? sz.width()
1172 : sz.height();
1173 } else {
1174 d->sizes[section] = size;
1175 }
1176
1177 int newHeight = (orient == Qt::Horizontal) ? sz.height() : sz.width();
1178 if (newHeight > d->height) {
1179 d->height = newHeight;
1180 } else if (newHeight < d->height) {
1181 /*
1182 We could be smarter, but we aren't. This makes a difference
1183 only for users with many columns and '\n's in their headers
1184 at the same time.
1185 */
1186 d->heightDirty = true;
1187 }
1188}
1189
1190/*!
1191 Adds a new section with label text \a s. Returns the index
1192 position where the section was added (at the right for horizontal
1193 headers, at the bottom for vertical headers). The section's width
1194 is set to \a size. If \a size \< 0, an appropriate size for the
1195 text \a s is chosen.
1196*/
1197int Q3Header::addLabel(const QString &s, int size)
1198{
1199 int n = ++d->count;
1200 if ((int)d->icons.size() < n )
1201 d->icons.resize(n);
1202 if ((int)d->sizes.size() < n ) {
1203 d->labels.resize(n);
1204 d->nullStringLabels.resize(n);
1205 d->sizes.resize(n);
1206 d->positions.resize(n);
1207 d->i2s.resize(n);
1208 d->s2i.resize(n);
1209 d->clicks.resize(n);
1210 d->resize.resize(n);
1211 }
1212 int section = d->count - 1;
1213 if (!d->is_a_table_header || !s.isNull()) {
1214 d->labels.insert(section, s);
1215 d->nullStringLabels.setBit(section, s.isNull());
1216 }
1217
1218 if (size >= 0 && s.isNull() && d->is_a_table_header) {
1219 d->sizes[section] = size;
1220 } else {
1221 d->sizes[section] = -1;
1222 setSectionSizeAndHeight(section, size);
1223 }
1224
1225 int index = section;
1226 d->positions[index] = d->lastPos;
1227
1228 d->s2i[section] = index;
1229 d->i2s[index] = section;
1230 d->clicks.setBit(section, d->clicks_default);
1231 d->resize.setBit(section, d->resize_default);
1232
1233 if (updatesEnabled()) {
1234 updateGeometry();
1235 calculatePositions();
1236 update();
1237 }
1238 return index;
1239}
1240
1241void Q3Header::resizeArrays(int size)
1242{
1243 d->icons.resize(size);
1244 d->labels.resize(size);
1245 d->nullStringLabels.resize(size);
1246 d->sizes.resize(size);
1247 d->positions.resize(size);
1248 d->i2s.resize(size);
1249 d->s2i.resize(size);
1250 d->clicks.resize(size);
1251 d->resize.resize(size);
1252}
1253
1254void Q3Header::setIsATableHeader(bool b)
1255{
1256 d->is_a_table_header = b;
1257}
1258
1259/*! \reimp */
1260QSize Q3Header::sizeHint() const
1261{
1262 int width;
1263 int height;
1264
1265 ensurePolished();
1266 QFontMetrics fm = fontMetrics();
1267
1268 if (d->heightDirty) {
1269 d->height = fm.lineSpacing() + 6;
1270 for (int i = 0; i < count(); i++) {
1271 int h = orient == Qt::Horizontal ?
1272 sectionSizeHint(i, fm).height() : sectionSizeHint(i, fm).width();
1273 d->height = qMax(d->height, h);
1274 }
1275 d->heightDirty = false;
1276 }
1277
1278 if (orient == Qt::Horizontal) {
1279 height = fm.lineSpacing() + 6;
1280 width = 0;
1281 height = qMax(height, d->height);
1282 for (int i = 0; i < count(); i++)
1283 width += d->sizes[i];
1284 } else {
1285 width = fm.width(QLatin1Char(' '));
1286 height = 0;
1287 width = qMax(width, d->height);
1288 for (int i = 0; i < count(); i++)
1289 height += d->sizes[i];
1290 }
1291 QStyleOptionHeader opt = getStyleOption(this, 0);
1292 return style()->sizeFromContents(QStyle::CT_Q3Header, &opt, QSize(width, height),
1293 this).expandedTo(QApplication::globalStrut());
1294}
1295
1296/*!
1297 \property Q3Header::offset
1298 \brief the header's left-most (or top-most) visible pixel
1299
1300 Setting this property will scroll the header so that \e offset
1301 becomes the left-most (or top-most for vertical headers) visible
1302 pixel.
1303*/
1304int Q3Header::offset() const
1305{
1306 if (reverse())
1307 return d->lastPos - width() - offs;
1308 return offs;
1309}
1310
1311void Q3Header::setOffset(int x)
1312{
1313 int oldOff = offset();
1314 offs = x;
1315 if(d->lastPos < (orient == Qt::Horizontal ? width() : height()))
1316 offs = 0;
1317 else if (reverse())
1318 offs = d->lastPos - width() - x;
1319 if (orient == Qt::Horizontal)
1320 scroll(oldOff-offset(), 0);
1321 else
1322 scroll(0, oldOff-offset());
1323}
1324
1325
1326
1327/*
1328 Returns the position of actual division line \a i in widget
1329 coordinates. May return a position outside the widget.
1330
1331 Note that the last division line is numbered count(). (There is one
1332 more line than the number of sections).
1333*/
1334int Q3Header::pPos(int i) const
1335{
1336 int pos;
1337 if (i == count())
1338 pos = d->lastPos;
1339 else
1340 pos = d->positions[i];
1341 if (reverse())
1342 pos = d->lastPos - pos;
1343 return pos - offset();
1344}
1345
1346
1347/*
1348 Returns the size of the section at index position \a i.
1349*/
1350int Q3Header::pSize(int i) const
1351{
1352 return d->sizes[d->i2s[i]];
1353}
1354
1355/*!
1356 Use mapToSection() instead.
1357
1358 Translates from actual index \a a (index at which the section is displayed) to
1359 logical index of the section. Returns -1 if \a a is outside the legal range.
1360
1361 \sa mapToActual()
1362*/
1363
1364int Q3Header::mapToLogical(int a) const
1365{
1366 return mapToSection(a);
1367}
1368
1369
1370/*!
1371 Use mapToIndex() instead.
1372
1373 Translates from logical index \a l to actual index (index at which the section \a l is displayed) .
1374 Returns -1 if \a l is outside the legal range.
1375
1376 \sa mapToLogical()
1377*/
1378
1379int Q3Header::mapToActual(int l) const
1380{
1381 return mapToIndex(l);
1382}
1383
1384
1385/*!
1386 Use resizeSection() instead.
1387
1388 Sets the size of the section \a section to \a s pixels.
1389
1390 \warning does not repaint or send out signals
1391*/
1392
1393void Q3Header::setCellSize(int section, int s)
1394{
1395 if (section < 0 || section >= count())
1396 return;
1397 d->sizes[section] = s;
1398 if (updatesEnabled())
1399 calculatePositions();
1400 else
1401 d->positionsDirty = true;
1402}
1403
1404
1405/*!
1406 If \a enable is true the user may resize section \a section;
1407 otherwise the section may not be manually resized.
1408
1409 If \a section is negative (the default) then the \a enable value
1410 is set for all existing sections and will be applied to any new
1411 sections that are added.
1412 Example:
1413 \snippet doc/src/snippets/code/src_qt3support_widgets_q3header.cpp 0
1414
1415 If the user resizes a section, a sizeChange() signal is emitted.
1416
1417 \sa setMovingEnabled() setClickEnabled() setTracking()
1418*/
1419
1420void Q3Header::setResizeEnabled(bool enable, int section)
1421{
1422 if (section < 0) {
1423 d->resize.fill(enable);
1424 // and future ones...
1425 d->resize_default = enable;
1426 } else if (section < count()) {
1427 d->resize[section] = enable;
1428 }
1429}
1430
1431
1432/*!
1433 \property Q3Header::moving
1434 \brief whether the header sections can be moved
1435
1436 If this property is true (the default) the user can move sections.
1437 If the user moves a section the indexChange() signal is emitted.
1438
1439 \sa setClickEnabled(), setResizeEnabled()
1440*/
1441
1442void Q3Header::setMovingEnabled(bool enable)
1443{
1444 d->move = enable;
1445}
1446
1447
1448/*!
1449 If \a enable is true, any clicks on section \a section will result
1450 in clicked() signals being emitted; otherwise the section will
1451 ignore clicks.
1452
1453 If \a section is -1 (the default) then the \a enable value is set
1454 for all existing sections and will be applied to any new sections
1455 that are added.
1456
1457 \sa setMovingEnabled(), setResizeEnabled()
1458*/
1459
1460void Q3Header::setClickEnabled(bool enable, int section)
1461{
1462 if (section < 0) {
1463 d->clicks.fill(enable);
1464 // and future ones...
1465 d->clicks_default = enable;
1466 } else if (section < count()) {
1467 d->clicks[section] = enable;
1468 }
1469}
1470
1471
1472/*!
1473 Paints the section at position \a index, inside rectangle \a fr
1474 (which uses widget coordinates) using painter \a p.
1475
1476 Calls paintSectionLabel().
1477*/
1478
1479void Q3Header::paintSection(QPainter *p, int index, const QRect& fr)
1480{
1481 int section = mapToSection(index);
1482 QStyleOptionHeader opt = getStyleOption(this, section);
1483 opt.state |= QStyle::State_Raised;
1484 opt.rect = fr;
1485
1486 if (section < 0) {
1487 style()->drawControl(QStyle::CE_Header, &opt, p, this);
1488 return;
1489 }
1490
1491 if (sectionSize(section) <= 0)
1492 return;
1493
1494 opt.state = (orient == Qt::Horizontal ? QStyle::State_Horizontal : QStyle::State_None);
1495 if (d->sortSection == section)
1496 opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
1497
1498 if (isEnabled())
1499 opt.state |= QStyle::State_Enabled;
1500 if (isClickEnabled(section) && (state == Pressed || state == Moving) && index == handleIdx)
1501 opt.state |= QStyle::State_Sunken; //currently pressed
1502 if (!(opt.state & QStyle::State_Sunken))
1503 opt.state |= QStyle::State_Raised;
1504 p->setBrushOrigin(fr.topLeft());
1505 if (d->clicks[section]) {
1506 style()->drawControl(QStyle::CE_Header, &opt, p, this);
1507 } else {
1508 p->save();
1509 p->setClipRect(fr); // hack to keep styles working
1510 opt.rect.setRect(fr.x() + 1, fr.y(), fr.width(), fr.height());
1511 style()->drawControl(QStyle::CE_Header, &opt, p, this);
1512 if (orient == Qt::Horizontal) {
1513 p->setPen(palette().color(QPalette::Mid));
1514 p->drawLine(fr.x() - 1, fr.y() + fr.height() - 1,
1515 fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1516 p->drawLine(fr.x() + fr.width() - 1, fr.y(),
1517 fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1518 } else {
1519 p->setPen(palette().color(QPalette::Mid));
1520 p->drawLine(fr.x() + width() - 1, fr.y(),
1521 fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1522 p->drawLine(fr.x(), fr.y() + fr.height() - 1,
1523 fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1524 p->setPen(palette().color(QPalette::Light));
1525 if (index > 0)
1526 p->drawLine(fr.x(), fr.y(), fr.x() + fr.width() - 1, fr.y());
1527 if (index == count() - 1) {
1528 p->drawLine(fr.x(), fr.y() + fr.height() - 1,
1529 fr.x() + fr.width() - 1, fr.y() + fr.height() - 1);
1530 p->setPen(palette().color(QPalette::Mid));
1531 p->drawLine(fr.x(), fr.y() + fr.height() - 2,
1532 fr.x() + fr.width() - 1, fr.y() + fr.height() - 2);
1533 }
1534 }
1535 p->restore();
1536 }
1537}
1538
1539/*!
1540 Paints the label of the section at position \a index, inside
1541 rectangle \a fr (which uses widget coordinates) using painter \a
1542 p.
1543
1544 Called by paintSection()
1545*/
1546void Q3Header::paintSectionLabel(QPainter *p, int index, const QRect& fr)
1547{
1548 int section = mapToSection(index);
1549 if (section < 0)
1550 return;
1551
1552 int dx = 0, dy = 0;
1553 QStyleOptionHeader opt = getStyleOption(this, section);
1554 if (d->sortSection == section)
1555 opt.sortIndicator = d->sortDirection ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;
1556 if (index == handleIdx && (state == Pressed || state == Moving)) {
1557 dx = style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &opt, this);
1558 dy = style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &opt, this);
1559 opt.state |= QStyle::State_Sunken;
1560 }
1561 if (isEnabled())
1562 opt.state |= QStyle::State_Enabled;
1563
1564
1565 opt.rect.setRect(fr.x() + style()->pixelMetric(QStyle::PM_HeaderMargin) + dx, fr.y() + 2 + dy,
1566 fr.width() - 6, fr.height() - 4);
1567
1568 style()->drawControl(QStyle::CE_HeaderLabel, &opt, p, this);
1569
1570 int arrowWidth = (orient == Qt::Horizontal ? height() : width()) / 2;
1571 int arrowHeight = fr.height() - 6;
1572 QSize ssh = sectionSizeHint(section, p->fontMetrics());
1573 int tw = (orient == Qt::Horizontal ? ssh.width() : ssh.height());
1574 int ew = 0;
1575
1576 if (style()->styleHint(QStyle::SH_Header_ArrowAlignment, 0, this) & Qt::AlignRight)
1577 ew = fr.width() - tw - 8;
1578 if (d->sortSection == section && tw <= fr.width()) {
1579 if (reverse()) {
1580 tw = fr.width() - tw;
1581 ew = fr.width() - ew - tw;
1582 }
1583 opt.state = QStyle::State_None;
1584 if (isEnabled())
1585 opt.state |= QStyle::State_Enabled;
1586 if (d->sortDirection)
1587 opt.state |= QStyle::State_DownArrow;
1588 else
1589 opt.state |= QStyle::State_UpArrow;
1590 QRect ar(fr.x() + tw - arrowWidth - 6 + ew, 4, arrowWidth, arrowHeight);
1591 if (label(section).isRightToLeft())
1592 ar.moveBy( 2*(fr.right() - ar.right()) + ar.width() - fr.width(), 0 );
1593 opt.rect = ar;
1594 style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &opt, p, this);
1595 }
1596}
1597
1598
1599/*! \reimp */
1600void Q3Header::paintEvent(QPaintEvent *e)
1601{
1602 QPainter p(this);
1603 p.setPen(palette().buttonText().color());
1604 int pos = orient == Qt::Horizontal ? e->rect().left() : e->rect().top();
1605 int id = mapToIndex(sectionAt(pos + offset()));
1606 if (id < 0) {
1607 if (pos > 0)
1608 id = d->count;
1609 else if (reverse())
1610 id = d->count - 1;
1611 else
1612 id = 0;
1613 }
1614 if (reverse()) {
1615 for (int i = id; i >= 0; i--) {
1616 QRect r = sRect(i);
1617 paintSection(&p, i, r);
1618 if (r.right() >= e->rect().right())
1619 return;
1620 }
1621 } else {
1622 if (count() > 0) {
1623 for (int i = id; i <= count(); i++) {
1624 QRect r = sRect(i);
1625 /*
1626 If the last section is clickable (and thus is
1627 painted raised), draw the virtual section count()
1628 as well. Otherwise it looks ugly.
1629 */
1630 if (i < count() || d->clicks[mapToSection(count() - 1)])
1631 paintSection(&p, i, r);
1632 if (hasFocus() && d->focusIdx == i) {
1633 QStyleOptionFocusRect opt;
1634 opt.rect.setRect(r.x()+2, r.y()+2, r.width()-4, r.height()-4);
1635 opt.palette = palette();
1636 opt.state = QStyle::State_None;
1637 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, &p, this);
1638 }
1639 if ((orient == Qt::Horizontal && r. right() >= e->rect().right())
1640 || (orient == Qt::Vertical && r. bottom() >= e->rect().bottom()))
1641 return;
1642 }
1643 }
1644 }
1645}
1646
1647/*!
1648 \overload
1649
1650 Sets the sort indicator to \a ascending. Use the other overload instead.
1651*/
1652
1653void Q3Header::setSortIndicator(int section, bool ascending)
1654{
1655 d->sortSection = section;
1656 if (section != -1)
1657 oldHandleIdx = section;
1658 d->sortDirection = ascending;
1659 update();
1660 updateGeometry();
1661}
1662
1663/*!
1664 \fn void Q3Header::setSortIndicator(int section, Qt::SortOrder order)
1665
1666 Sets a sort indicator onto the specified \a section. The indicator's
1667 \a order is either Ascending or Descending.
1668
1669 Only one section can show a sort indicator at any one time. If you
1670 don't want any section to show a sort indicator pass a \a section
1671 number of -1.
1672
1673 \sa sortIndicatorSection(), sortIndicatorOrder()
1674*/
1675
1676/*!
1677 Returns the section showing the sort indicator or -1 if there is no sort indicator.
1678
1679 \sa setSortIndicator(), sortIndicatorOrder()
1680*/
1681
1682int Q3Header::sortIndicatorSection() const
1683{
1684 return d->sortSection;
1685}
1686
1687/*!
1688 Returns the implied sort order of the Q3Headers sort indicator.
1689
1690 \sa setSortIndicator(), sortIndicatorSection()
1691*/
1692
1693Qt::SortOrder Q3Header::sortIndicatorOrder() const
1694{
1695 return d->sortDirection ? Qt::AscendingOrder : Qt::DescendingOrder;
1696}
1697
1698/*!
1699 Resizes section \a section to \a s pixels wide (or high).
1700*/
1701
1702void Q3Header::resizeSection(int section, int s)
1703{
1704 setCellSize(section, s);
1705 update();
1706}
1707
1708/*!
1709 Returns the width (or height) of the \a section in pixels.
1710*/
1711
1712int Q3Header::sectionSize(int section) const
1713{
1714 if (section < 0 || section >= count())
1715 return 0;
1716 return d->sizes[section];
1717}
1718
1719/*!
1720 Returns the position (in pixels) at which the \a section starts.
1721
1722 \sa offset()
1723*/
1724
1725int Q3Header::sectionPos(int section) const
1726{
1727 if (d->positionsDirty)
1728 ((Q3Header *)this)->calculatePositions();
1729 if (section < 0 || section >= count() )
1730 return 0;
1731 return d->positions[d->s2i[section]];
1732}
1733
1734/*!
1735 Returns the index of the section which contains the position \a
1736 pos given in pixels from the left (or top).
1737
1738 \sa offset()
1739*/
1740
1741int Q3Header::sectionAt(int pos) const
1742{
1743 if (reverse())
1744 pos = d->lastPos - pos;
1745 return d->sectionAt(pos);
1746}
1747
1748/*!
1749 Returns the number of the section that is displayed at index
1750 position \a index.
1751*/
1752
1753int Q3Header::mapToSection(int index) const
1754{
1755 return (index >= 0 && index < count()) ? d->i2s[index] : -1;
1756}
1757
1758/*!
1759 Returns the index position at which section \a section is
1760 displayed.
1761*/
1762
1763int Q3Header::mapToIndex(int section) const
1764{
1765 return (section >= 0 && section < count()) ? d->s2i[section] : -1;
1766}
1767
1768/*!
1769 Moves section \a section to index position \a toIndex.
1770*/
1771
1772void Q3Header::moveSection(int section, int toIndex)
1773{
1774 int fromIndex = mapToIndex(section);
1775 if (fromIndex == toIndex ||
1776 fromIndex < 0 || fromIndex > count() ||
1777 toIndex < 0 || toIndex > count())
1778 return;
1779 int i;
1780 int idx = d->i2s[fromIndex];
1781 if (fromIndex < toIndex) {
1782 for (i = fromIndex; i < toIndex - 1; i++) {
1783 int t;
1784 d->i2s[i] = t = d->i2s[i+1];
1785 d->s2i[t] = i;
1786 }
1787 d->i2s[toIndex-1] = idx;
1788 d->s2i[idx] = toIndex-1;
1789 } else {
1790 for (i = fromIndex; i > toIndex; i--) {
1791 int t;
1792 d->i2s[i] = t = d->i2s[i-1];
1793 d->s2i[t] = i;
1794 }
1795 d->i2s[toIndex] = idx;
1796 d->s2i[idx] = toIndex;
1797 }
1798 calculatePositions();
1799}
1800
1801/*!
1802 Returns true if section \a section is clickable; otherwise returns
1803 false.
1804
1805 If \a section is out of range (negative or larger than count() -
1806 1): returns true if all sections are clickable; otherwise returns
1807 false.
1808
1809 \sa setClickEnabled()
1810*/
1811
1812bool Q3Header::isClickEnabled(int section) const
1813{
1814 if (section >= 0 && section < count()) {
1815 return (bool)d->clicks[section];
1816 }
1817
1818 for (int i = 0; i < count(); ++i) {
1819 if (!d->clicks[i])
1820 return false;
1821 }
1822 return true;
1823}
1824
1825/*!
1826 Returns true if section \a section is resizeable; otherwise
1827 returns false.
1828
1829 If \a section is -1 then this function applies to all sections,
1830 i.e. returns true if all sections are resizeable; otherwise
1831 returns false.
1832
1833 \sa setResizeEnabled()
1834*/
1835
1836bool Q3Header::isResizeEnabled(int section) const
1837{
1838 if (section >= 0 && section < count()) {
1839 return (bool)d->resize[section];
1840 }
1841
1842 for (int i = 0; i < count();++i) {
1843 if (!d->resize[i])
1844 return false;
1845 }
1846 return true;
1847}
1848
1849bool Q3Header::isMovingEnabled() const
1850{
1851 return d->move;
1852}
1853
1854/*! \internal */
1855
1856void Q3Header::setUpdatesEnabled(bool enable)
1857{
1858 if (enable)
1859 calculatePositions();
1860 QWidget::setUpdatesEnabled(enable);
1861}
1862
1863
1864bool Q3Header::reverse () const
1865{
1866#if 0
1867 return (orient == Qt::Horizontal && QApplication::reverseLayout());
1868#else
1869 return false;
1870#endif
1871}
1872
1873/*! \reimp */
1874void Q3Header::resizeEvent(QResizeEvent *e)
1875{
1876 if (e)
1877 QWidget::resizeEvent(e);
1878
1879 if(d->lastPos < width()) {
1880 offs = 0;
1881 }
1882
1883 if (e) {
1884 adjustHeaderSize(orientation() == Qt::Horizontal ?
1885 width() - e->oldSize().width() : height() - e->oldSize().height());
1886 if ((orientation() == Qt::Horizontal && height() != e->oldSize().height())
1887 || (orientation() == Qt::Vertical && width() != e->oldSize().width()))
1888 update();
1889 } else
1890 adjustHeaderSize();
1891}
1892
1893/*!
1894 \fn void Q3Header::adjustHeaderSize()
1895
1896 Adjusts the size of the sections to fit the size of the header as
1897 completely as possible. Only sections for which isStretchEnabled()
1898 is true will be resized.
1899*/
1900
1901void Q3Header::adjustHeaderSize(int diff)
1902{
1903 if (!count())
1904 return;
1905
1906 // we skip the adjustHeaderSize when trying to resize the last column which is set to stretchable
1907 if (d->fullSize == (count() -1) &&
1908 (d->lastPos - d->sizes[count() -1]) > (orient == Qt::Horizontal ? width() : height()))
1909 return;
1910
1911 if (d->fullSize >= 0) {
1912 int sec = mapToSection(d->fullSize);
1913 int lsec = mapToSection(count() - 1);
1914 int ns = sectionSize(sec) +
1915 (orientation() == Qt::Horizontal ?
1916 width() : height()) - (sectionPos(lsec) + sectionSize(lsec));
1917 int os = sectionSize(sec);
1918 if (ns < 20)
1919 ns = 20;
1920 setCellSize(sec, ns);
1921 repaint();
1922 emit sizeChange(sec, os, ns);
1923 } else if (d->fullSize == -1) {
1924 int df = diff / count();
1925 int part = orientation() == Qt::Horizontal ? width() / count() : height() / count();
1926 for (int i = 0; i < count() - 1; ++i) {
1927 int sec = mapToIndex(i);
1928 int os = sectionSize(sec);
1929 int ns = diff != -1 ? os + df : part;
1930 if (ns < 20)
1931 ns = 20;
1932 setCellSize(sec, ns);
1933 emit sizeChange(sec, os, ns);
1934 }
1935 int sec = mapToIndex(count() - 1);
1936 int ns = (orientation() == Qt::Horizontal ? width() : height()) - sectionPos(sec);
1937 int os = sectionSize(sec);
1938 if (ns < 20)
1939 ns = 20;
1940 setCellSize(sec, ns);
1941 repaint();
1942 emit sizeChange(sec, os, ns);
1943 }
1944}
1945
1946/*!
1947 Returns the total width of all the header columns.
1948*/
1949int Q3Header::headerWidth() const
1950{
1951 if (d->pos_dirty) {
1952 ((Q3Header*)this)->calculatePositions();
1953 d->pos_dirty = false;
1954 }
1955 return d->lastPos;
1956}
1957
1958void Q3Header::calculatePositions(bool onlyVisible, int start)
1959{
1960 d->positionsDirty = false;
1961 d->lastPos = count() > 0 ? d->positions[start] : 0;
1962 for (int i = start; i < count(); i++) {
1963 d->positions[i] = d->lastPos;
1964 d->lastPos += d->sizes[d->i2s[i]];
1965 if (onlyVisible && d->lastPos > offset() +
1966 (orientation() == Qt::Horizontal ? width() : height()))
1967 break;
1968 }
1969 d->pos_dirty = onlyVisible;
1970}
1971
1972
1973/*!
1974 \property Q3Header::stretching
1975 \brief whether the header sections always take up the full width
1976 (or height) of the header
1977*/
1978
1979
1980/*!
1981 If \a b is true, section \a section will be resized when the
1982 header is resized, so that the sections take up the full width (or
1983 height for vertical headers) of the header; otherwise section \a
1984 section will be set to be unstretchable and will not resize when
1985 the header is resized.
1986
1987 If \a section is -1, and if \a b is true, then all sections will
1988 be resized equally when the header is resized so that they take up
1989 the full width (or height for vertical headers) of the header;
1990 otherwise all the sections will be set to be unstretchable and
1991 will not resize when the header is resized.
1992
1993 \sa adjustHeaderSize()
1994*/
1995
1996void Q3Header::setStretchEnabled(bool b, int section)
1997{
1998 if (b)
1999 d->fullSize = section;
2000 else
2001 d->fullSize = -2;
2002 adjustHeaderSize();
2003}
2004
2005bool Q3Header::isStretchEnabled() const
2006{
2007 return d->fullSize == -1;
2008}
2009
2010/*!
2011 \overload
2012
2013 Returns true if section \a section will resize to take up the full
2014 width (or height) of the header; otherwise returns false. If at
2015 least one section has stretch enabled the sections will always
2016 take up the full width of the header.
2017
2018 \sa setStretchEnabled()
2019*/
2020
2021bool Q3Header::isStretchEnabled(int section) const
2022{
2023 return d->fullSize == section;
2024}
2025
2026/*!
2027 \reimp
2028*/
2029void Q3Header::changeEvent(QEvent *ev)
2030{
2031 if(ev->type() == QEvent::FontChange) {
2032 QFontMetrics fm = fontMetrics();
2033 d->height = (orient == Qt::Horizontal) ? fm.lineSpacing() + 6 : fm.width(QLatin1Char(' '));
2034 }
2035 QWidget::changeEvent(ev);
2036}
2037
2038QT_END_NAMESPACE
2039
2040#endif // QT_NO_HEADER
Note: See TracBrowser for help on using the repository browser.