source: trunk/src/widgets/qheader.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

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